I'm currently working on an architecture and patterns for our next project. I'm considering using DDD, but since the project will be of medium scale, I'm trying to make it as simple as possible from the perspective of code duplication and overall maintenance.
The layers look as follows, basically each layer is a separate assembly:
DB -> Domain -> Application -> Web API -> Clients
For DB access I'm using EF Core 1.0.
Here is my current design (simplified) of the classes in data and domain layers and which I'm not very happy with.
Domain:
class Task
{
private int State { get; set; }
private string Description { get; set; }
private int CreatedById { get; set; }
}
Data (EF):
class TaskData
{
public int State { get; set; }
public string Description { get; set; }
public int CreatedById { get; set; }
public User CreatedBy { get; set; }
}
Ideally I would like to use my domain entity directly with the ORM, but the Task and TaskData are not identical. In Task entity I don't need the navigation property CreatedBy, I'm fine with just an Id, so I don't want to pollute the domain with stuff it doesn't care about.
In data model I'm using the navigation property for some reports, so this join is useful in some cases.
As you can see, if I can't map domain entities directly, I have to do some mapping in the data layer. To be more specific, in the repository via. manual mapper class. Because my domain entity has no public getters and setters, I can't map TaskData to Task entity on a property basis.
This leads me to memento pattern, so I create new class, which seems to be just plain DTO:
class TaskSnapshot
{
public int State { get; set; }
public string Description { get; set; }
public int CreatedById { get; set; }
}
And my original Task entity looks now like:
class Task
{
...
public Task(TaskSnapshot snapshot)
{
this.State = snapshot.State;
this.Description = snapshot.Description;
this.CreateById = snapshot.CreatedById;
}
public TaskSnapshot ToSnapshot()
{
return new TaskSnapshot()
{
State = this.State,
Description = this.Description,
CreatedBy = this.CreatedBy };
}
}
As you can see, it takes three classes with different purpose, but very similar content to create and maintain. And it's just data and domain layers. The "duplication" continues in other layers too.
Now when I decide to add a new field, I need to remember all the places where to add it and also correctly assign. I'm afraid this will often lead to bugs, because someone from the team just forgets to update all the code.
What I could do:
Make getters and setters public on the domain entities, so I wouldn't need Snapshots. -> Definitely no!
Add unnecessary properties (CreatedBy navigation) to the Task entity and use it directly with ORM. -> I would rather not.
Transform Snapshot classes to data model and use them with ORM. -> I probably wouldn't mind navigation properties there, but it would mean that data model is part of the domain assembly. -> I don't know, I don't like it.
Is there a recommendation how could I effectively reduce number of classes or concentrate ALL the assignments (mapping) to one place/mapper class without compromising domain?
Thank you.