Integrating a business rules engine does not come for free, you do not simply plug it in and then write any business rules you want. Instead, usually each type of business rule you want to implement needs to have code written in the underlying product to support. For example, a fact has to be loaded into the engine before a rule can use it.
This is why it is useless to integrate a BRE until you have an actual use case. And that use case must include fairly complicate business rules that change frequently. In any other case, I believe you are better served by writing the logic you want in the code, and wait for the configuration use-case (with well defined expecations) to come along.
You can take either approach and have it work well - there are, of course, pros and cons.
Entity Framework is definitely intended to suffuse your domain entities. It does work well when your domain entities and data entities are the same classes. It's much nicer if you can rely on EF to keep track of the changes for you, and just call context.SaveChanges()
when you're finished with your transactional work. It also means that your validation attributes don't have to be set twice, once on your domain models and once on your persisted entities - things like [Required]
or [StringLength(x)]
can be checked in your business logic, allowing you to catch invalid data states before you try to do a DB transaction and get an EntityValidationException
. Finally, it's quick to code - you don't need to write a mapping layer or repository, but can instead work directly with the EF context. It's already a repository and a unit of work, so extra layers of abstraction don't accomplish anything.
A downside to combining your domain and persisted entities is that you end up with a bunch of [NotMapped]
attributes scattered throughout your properties. Often, you will want domain-specific properties which are either get-only filters on persisted data, or are set later in your business logic and not persisted back into your database. Some times, you'll want to express your data in your domain models in a way that doesn't work very well with a database - for example, when using enums, Entity will map these to an int
column - but perhaps you want to map them to a human-readable string
, so you don't need to consult a lookup when examining the database. Then you end up with a string
property which is mapped, an enum
property which isn't (but gets the string and maps to the enum), and an API which exposes both! Similarly, if you want to combine complex types (tables) across contexts, you may wind up with a mapped
OtherTableId and an unmapped ComplexType
property, both of which are exposed as public on your class. This can be confusing for someone who isn't familiar with the project, and unnecessarily bloats your domain models.
The more complex my business logic/domain, the more restrictive or cumbersome I find combining my domain and persisted entities to be. For projects with short deadlines, or that don't express a complex business layer, I feel that using EF entities for both purposes is appropriate, and there's no need to abstract your domain away from your persistence. For projects which need maximum ease-of-extension, or that need to express very complicated logic, I think you're better off separating the two and dealing with the extra persistence complexity.
One trick to avoiding the trouble of manually tracking your entity changes is to store the corresponding persisted entity ID in your domain model. This can be filled automatically by your mapping layer. Then, when you need to persist a change back to EF, retrieve the relevant persistent entity before doing any mapping. Then when you map the changes, EF will detect them automatically, and you can call context.SaveChanges()
without having to track them by hand.
public class OrganisationService
{
public void PersistLicenses(IList<DomainLicenses> licenses)
{
using (var context = new EFContext())
{
foreach (DomainLicense license in licenses)
{
var persistedLicense = context.Licenses.Find(pl => pl.Id == license.PersistedId);
MappingService.Map(persistedLicense, license); //Right-left mapping
context.Update(persistedLicense);
}
context.SaveChanges();
}
}
}
Best Answer
What would you do if the message was in a database? Do the exact same thing. A web service is just another storage device, in essence.
Your calling code should know nothing of the repository implementation details, if at all possible. Database, text file, web service, it's all GetMessage().