DDD, which stands for Domain Driven Design, generated a lot of buzz. There are a lot of articles and blog posts on how DDD works with this or that technology. Or how to build an application using DDD and X? It’s natural that I also got interested and decided to see how it works with the Entity Framework.
Brief introduction to Domain Driven Design
DDD is an architectural concept of creating software. The main idea is that the design of an application should be based on a model, not on technology or anything else. This makes it clear not only for developers but also for specialists in the field.
Domain Driven Design is based on five objects:
- Entities: These are the objects that represent the core of an application. They express the business, and are understood by clients. What’s more important is that each Entity has an identity and it’s unique within the system.
- Value Objects: These objects don’t have identities and describe characteristics.
- Services: Services contain logic that dosn’t belong to a particular Entity.
- Factories: This is just a well known pattern, which takes care of creating Entities.
- Repositories: Repositories are used to separate Entities and their representation in a database. They contain code which is responsible for interaction with a database.
Here is DDD on Wiki.
Just a couple of words about what the Entity Framework (EF) is. According to Wikipedia, the Entity Framework is neither more nor less than an ORM. One shouldn’t need a PhD to understand that the datasets based approach of handling database persistence is not always working well, especially in big projects. In fact, EF is the first ORM from Microsoft (LINQ to SQL doesn’t count).
It’s easy to guess by the name of the framework that the core concept of EF is… the EntityObject. EntityObjects represent objects which contain information that should be persisted. To make it work, there are three XAML-based model and mapping files which are used by EF to work with actual data.
From a user’s point of view, EF makes persistence tasks very simple. Visual Studio generates all necessary EntityObject classes from a database, and instead of working with
DataSets and creating
SqlConnections, the user works with classes which represent business objects. Here is Entity Framework on MSDN.
DDD Entities vs. EF EntityObjects
When someone thinks about creating a new application using DDD and EF, the first idea which occurs is to use EF EntityObjects as DDD Entities.
EntityObjects are unique and do have identities, as required for Entities. However, they can’t be separated from the Entity Framework. According to the DDD approach, domain objects should represent only business logic, they shouldn’t be responsible for storing or displaying themselves or anything else. This concept is also known as Persistence Ignorance, meaning that the domain objects are ignorant of how data is saved or retrieved from a data source.
Unfortunately, EF is not a PI ORM, and there is no way to make EntityObjects independent of the framework. Their code is auto generated based on a database schema and a mapping file. Theoretically, it’s possible to create “true” entities and use the EntityFramework only as a data persistence layer. However, it’s absolutely ridiculous because in this case it will be necessary to again implement all the persistence patterns provided by EF (Unit of Work, Identity Mapper, etc.).
So, in terms of DDD, the EntityFramework acts as Repositories and EntityObjects as its integrated Entities. It is, of course, a viable solution, but many prefer to have domain objects which do not know anything about an underlying data source.
Persistence Ignorance is something people have been arguing about for quite a while. Some think it’s good, some think it’s good but not always necessary.
These are the restrictions that a persistent-ignorant framework must not impose on the developer's domain objects:
- Require inheriting from a specified base class other than
- Require the use of a factory method to instantiate them
- Require the use of specialized data types for properties, including collections
- Require implementing particular interfaces
- Require the use of framework-specific constructors
- Require the use of specific fields or properties
- Prohibit specific application constructs
- Require writing RDBMS code, such as queries or Stored Procedure calls in a provider-specific dialect
Even if it’s nice to have an opportunity to swap databases, not everyone needs it. A more practical goal of PI is an ability to mock database objects easier, which simplifies Unit Testing.
The supporters of separation of domain objects and persistence logic were not happy by the approach suggested in EF. Their voices were heard, and it was promised that EF v2 will be PI compatible.
For the time being, it’s possible to use an intermediate solution available in EF. It’s the so called IPOCO pattern.
POCO stands for Plain Old DLR Object and IPOCO for Interface POCO. POCOs are objects which do not contain any complicated, framework specific code. For example, EntityObjects in Entity Framework aren’t POCOs, because there is a lot of ORM specific code. IPOCO is not an actual interface, it’s a pattern which requires implementing interfaces instead of deriving from special objects to use an ORM.
The first step of the Entity Framework team on the way to PI was introduction of IPOCO compatibility in the current version of the framework.
There are three interfaces to be implemented by custom classes.
IEntityWithChangeTracker for change tracking
IEntityWithKey for exposing the entity identity key (optional, but not implementing this interface causes a significant reduction in performance)
IEntityWithRelationships is required for entities with associations
If you decide to go this way and use IPOCO instead of classes generated by EdmGen.exe, you should be aware of the amount of work ahead of you. Not only will you have to implement the interfaces, but also decorate your classes with EDM specific attributes, to provide the framework with necessary metadata. Also, you will deprive yourself of the convenience of the automatic properties feature, because each property will need change-tracking functionality. Basically, everything you see in a generated EntityObject, you will have to do yourself.
To better understand the coding required, have a look at the MSDN article which has a sample implementation of all the interfaces.
Designing an application with EF
The lack of PI in EF doesn’t mean that it' is worthless. Not at all. It’s just a different approach.
Let’s pretend we are still doing DDD, only our domain objects will be dependant on the database layer. It’s not very good, but we will hope that we won’t need to use another database in the foreseeable future.
The first question which arises is where do we put domain specific logic? As EntityObjects are auto generated, it’s a bad idea to extend them directly; instead, we can create partial classes which will contain everything we need.
Don’t forget that EntityFramework is more than a set of auto generated classes. Its ObjectContext will be our Identity Map and Unit of Work, so our Repositories will be very simple. They will serve as storages for all database interaction code. It will void the entities of the code they don’t really need, and give as a chance to mock queries in Unit Tests.
We can undertake one more thing which will potentially help us if we decide to change the ORM. We can create interfaces and make our Entity objects to implement them. It will complicate our life a little bit, but we will have an illusion of “independency”, and who knows probably it will help one day… An example of this architecture can be found here.
If betraying the PI idea is not crucial, one can say that it’s possible to use EF and DDD together. If it is… there are always other options. You can achieve a greater level of abstraction for domain objects by using IPOCO, by waiting for EF v2, or by using another ORM (NHibernate is said to be fully PI).
- Professional ADO.NET 3.5 with LINQ and the Entity Framework by Roger Jennings
- Pro LINQ Object Relational Mapping with C# 2008 by Vijay P. Mehta