I'm currently working in a project where we are starting to build an application using a DDD approach. We are now looking into using Entity Framework 6 code first to help us with data persistence. My question is how to best handle data mapping between our domain objects and EF entities?
C# – Advice on mapping of entities to domain objects
cdomain-driven-designentity-framework
Related Solutions
Whereas one approach is to implement the ICloneable
interface (described here, so I won't regurgitate), here's a nice deep clone object copier I found on The Code Project a while ago and incorporated it into our code.
As mentioned elsewhere, it requires your objects to be serializable.
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
/// <summary>
/// Reference Article http://www.codeproject.com/KB/tips/SerializedObjectCloner.aspx
/// Provides a method for performing a deep copy of an object.
/// Binary Serialization is used to perform the copy.
/// </summary>
public static class ObjectCopier
{
/// <summary>
/// Perform a deep copy of the object via serialization.
/// </summary>
/// <typeparam name="T">The type of object being copied.</typeparam>
/// <param name="source">The object instance to copy.</param>
/// <returns>A deep copy of the object.</returns>
public static T Clone<T>(T source)
{
if (!typeof(T).IsSerializable)
{
throw new ArgumentException("The type must be serializable.", nameof(source));
}
// Don't serialize a null object, simply return the default for that object
if (ReferenceEquals(source, null)) return default;
using var Stream stream = new MemoryStream();
IFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, source);
stream.Seek(0, SeekOrigin.Begin);
return (T)formatter.Deserialize(stream);
}
}
The idea is that it serializes your object and then deserializes it into a fresh object. The benefit is that you don't have to concern yourself about cloning everything when an object gets too complex.
In case of you prefer to use the new extension methods of C# 3.0, change the method to have the following signature:
public static T Clone<T>(this T source)
{
// ...
}
Now the method call simply becomes objectBeingCloned.Clone();
.
EDIT (January 10 2015) Thought I'd revisit this, to mention I recently started using (Newtonsoft) Json to do this, it should be lighter, and avoids the overhead of [Serializable] tags. (NB @atconway has pointed out in the comments that private members are not cloned using the JSON method)
/// <summary>
/// Perform a deep Copy of the object, using Json as a serialization method. NOTE: Private members are not cloned using this method.
/// </summary>
/// <typeparam name="T">The type of object being copied.</typeparam>
/// <param name="source">The object instance to copy.</param>
/// <returns>The copied object.</returns>
public static T CloneJson<T>(this T source)
{
// Don't serialize a null object, simply return the default for that object
if (ReferenceEquals(source, null)) return default;
// initialize inner objects individually
// for example in default constructor some list property initialized with some values,
// but in 'source' these items are cleaned -
// without ObjectCreationHandling.Replace default constructor values will be added to result
var deserializeSettings = new JsonSerializerSettings {ObjectCreationHandling = ObjectCreationHandling.Replace};
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source), deserializeSettings);
}
There's a bit of a confusion here. Repositories access aggregate roots. Aggregate roots are entities. The reason for this is separation of concerns and good layering. This doesn't make sense on small projects, but if you're on a large team you want to say, "You access a product through the Product Repository. Product is an aggregate root for a collection of entities, including the ProductCatalog object. If you want to update the ProductCatalog you must go through the ProductRepository."
In this way you have very, very clear separation on the business logic and where things get updated. You don't have some kid who is off by himself and writes this entire program that does all these complicated things to the product catalog and when it comes to integrate it to the upstream project, you're sitting there looking at it and realize it all has to be ditched. It also means when people join the team, add new features, they know where to go and how to structure the program.
But wait! Repository also refers to the persistence layer, as in the Repository Pattern. In a better world an Eric Evans' Repository and the Repository Pattern would have separate names, because they tend to overlap quite a bit. To get the repository pattern you have contrast with other ways in which data is accessed, with a service bus or an event model system. Usually when you get to this level, the Eric Evans' Repository definition goes by the way side and you start talking about a bounded context. Each bounded context is essentially its own application. You might have a sophisticated approval system for getting things into the product catalog. In your original design the product was the center piece but in this bounded context the product catalog is. You still might access product information and update product via a service bus, but you must realize that a product catalog outside the bounded context might mean something completely different.
Back to your original question. If you're accessing a repository from within an entity it means the entity is really not a business entity but probably something that should exist in a service layer. This is because entities are business object and should concern themselves with being as much like a DSL (domain specific language) as possible. Only have business information in this layer. If you're troubleshooting a performance issue, you'll know to look elsewhere since only business information should be here. If suddenly, you have application issues here, you're making it very hard to extend and maintain an application, which is really the heart of DDD: making maintainable software.
Response to Comment 1: Right, good question. So not all validation occurs in the domain layer. Sharp has an attribute "DomainSignature" that does what you want. It is persistence aware, but being an attribute keeps the domain layer clean. It ensures that you don't have a duplicate entity with, in your example the same name.
But let's talk about more complicated validation rules. Let's say you're Amazon.com. Have you ever ordered something with an expired credit card? I have, where I haven't updated the card and bought something. It accepts the order and the UI informs me that everything is peachy. About 15 minutes later, I'll get an e-mail saying there's a problem with my order, my credit card is invalid. What's happening here is that, ideally, there's some regex validation in the domain layer. Is this a correct credit card number? If yes, persist the order. However, there's additional validation at the application tasks layer, where an external service is queried to see if payment can be made on the credit card. If not, don't actually ship anything, suspend the order and wait for the customer. This should all take place in a service layer.
Don't be afraid to create validation objects at the service layer that can access repositories. Just keep it out of the domain layer.
Related Topic
- Hibernate – the difference between DAO and Repository patterns
- Sql-server – What are the best practices for using a GUID as a primary key, specifically regarding performance
- C: Debugging options for gcc environments
- Java killing process after a period of time
- Asp – html helpers ASP.NET MVC DropDownList in .aspx in VB language
- C# – Entities VS Domain Models VS View Models
Best Answer
To keep your app and yourself sane on the long term, NEVER EVER start your DDD app with persistence related issues (what db, what orm etc) and ALWAYS (yes, always) touch the db as the last stage of the development.
Model your Domain and in fact any other model except persistence. Use the Repository pattern to keep the app decoupled from the Persistence. Define the repo interface as needed by the app and not tied to the db access method (that's why you're implementing the persistence later so you won't get tempted to couple your app to persistence details).
Write in-memory implementations for the repo interfaces, this usually means a simple wrapper over a list or dictionary so it's VERY fast to write and more importantly trivial to change. Use those to actually test and develop the app.
After the interfaces are stable and the app works then it's time to write the persistence implementation where you can use whatever you wish. In your case EF and there it comes the mapping.
Now, this is highly subjective there isn't a right or wrong way, there's the way YOU prefer doing things.
Personally, I have the habit of using mementos so I get the memento from the domain object and then manually mapping it to the (micro)ORM entities. The reason I'm doing it manually is because my mementos contain value objects. If I would be using AutoMapper I'd be needing to confingure it and in essence I'd be writing more code than doing it manually
Update (2015)
These days I just Json the object and either use a specific read model or store it directly in a read model with a
Data
column that contains the serialized object. I use Mementos only for very specific cases. < /update>Depending on how your domain objects look and how the EF entities look you might get away with using automapper for the majority of the mapping. You will have a harder time testing your repositories though.
It's up to you how you do it, find the way it suits your style and it's easily maintainable but NEVER EVER design or modify your domain objects to be more compatible or to match the ORM entities. It's not about changing databases or ORMs, it's about having the Domain (and the rest of the app) properly decoupled from the Persistence details (which the ORM is).
So resist the temptation to reuse things which are other layers' implementation details. The reason the application is structured in layers is because you want decoupling. Keep it that way.