5

I'm trying to get my head around DDD but I'm stuck. This is how I setup my project:

Data Access Layer
 -Entity models that map to the db
 -Db connection stuff
 -Repositories implementations

Domain Layer
 -Models that represent the DAL entity models
 -Repositories interfaces

Application Layer
 -MVC application that uses Domain models

The first problem I see here is that the Domain models are exactly the same as the Entity models, and I have a serious problem with this: the entity models obviously have validation configured into them, things like "max length", "nullable", "required", etc. Now, to conform to what I understand is DDD, I can't directly use these models anywhere, except the DAL, so I created my domain layer. In the domain layer, I have all these validation rules duplicated for UI validation, and what's even worse, is that if I need to change a rule, I will have to change it in two places: the DAL and the Domain.

Example:

User Entity in DAL
Name (required)
Last name (required)
Email (required, maxlen 120)
Username (required, maxlen 120)

User Domain Model
Name (required)
Last name (required)
Email (required, maxlen 120)
Username (required, maxlen 120)

Another thing that I find very weird is that the repositories organization in this architecture. Following what I read, I created a GenericRepository interface, and a UserRepository interface, which inherits the GenericRepository, all in the Domain layer. I implemented the GenericRepository in the DAL, and the implementation creates a DAO for the type of the entity used to create the repository. So far, so good.

Then, I proceeded to implement the UserRepository, and here I have another problem: the UserRepository interface expects the Domain User model, and when I try to implement the interface in the DAL, I need to implement it using the Domain User model, which causes the DAO to be created for a Domain model, not a DAL model, and this doesn't make any sense. The only to fix it would be to reference the DAL in the Domain layer, which is wrong.

Domain Layer:

public interface IGenericRepository<TEntity>
{
    TEntity FindById(TKey id);
}

public interface IUserRepository : IGenericRepository<Domain.User>
{
    Task<User> FindByUserNameAsync(string userName);
}


DAL:

public abstract class GenericRepository<TEntity> : IGenericRepository<TEntity>
{
    protected DbContext ctx;
    protected DAO<Entity> dao;

    public GenericRepository(DbContext context)
    {
        ctx = context;
        dao= ctx.Dao<TEntity>();
    }

    public virtual TEntity FindById(TKey id)
    {
        return dao.Find(id);
    }
}

 public class UserRepository : GenericRepository<Domain.Models.User>, IUserRepository
{
    public UserRepository(DbContext context)
        : base(context)
    {
       // THIS WILL CREATE A DAO FOR A DOMAIN MODEL
    }

    // rest of code...
}

Can anybody shed a light on what I'm missing from DDD?

0

3 Answers 3

7

Okay. There is a lot to unpack here, but the I think the root cause of some of your confusion is stemming from starting this process with the physical model at the forefront. This commonly causes all sorts of issues for individuals first trying to implement DDD. The goal of DDD is to model the behavior of a system such that the result is a useful abstraction of the function requirements of the core domain. For now, just forget about your DAL. It's an implementation detail.

Start by modelling your system by organizing the models by behavior into bounded contexts. It's the behavior of models that truly relates them. The data/attributes that entities contain is rarely a good starting point for modelling the functional requirements of a complex system. I'd love to provide some examples, but you didn't specify your domain so I'll just give you some tips:

Starting with your User domain model. I'd bet the kitchen sink that the only reason this exists is because you have a User entity. The issue with this is that User implies little to no behavior (uses what?), probably encompasses too much knowledge, and is therefore too abstract. What do your users do (authentication aside)? Shop? Shopper. Comment? Commenter/Poster. Sell? Seller. The important part is that these are not mutually exclusive! One User can be all of these things depending on the behavior in which they are going to engage. The fact that they all map to the same database table is an implementation detail.

You see where I'm going here? You may have a Shopping context that has Shopper, ShoppingCart, and CartItem and a Billing context that has Buyer, PurchaseRequest, and LineItem where the models in each respectively map to a [User], [Order], and [OrderItem] database table. Your model should be the absolute focus of this process. Not how it is persisted.

As for a direct answer to your question. There are a number common objects found in a domain. DomainModels and ValueObjects are the cornerstones of your model, but you will often find Repositories and Factories playing a supporting role. Because repositories and factories nearly always need to know the implementation details of your domain, they are usually part of your domain model (but not part of the diagram, for example).

5

It seems that you have seen someone implementing a DDD design using the architecture you mention in the question, but that is not what DDD is. That is just one of many possible implementations. I recommend you to follow up on king-side-slide answer regarding DDD.

Regarding your question, I see it more as a general Entity Framework and projects organisation question. And here are some comments:

Your domain models do not represent your DAL models. It's the other way around. You need to design your domain models to represent some concepts in your domain. Then your DAL is just a utility to persist these models. Personally, I don't use DAL entities at all. EF can map your domain models directly. Use mapping files with EntityTypeConfiguration instead of attributes, which contaminate your model or fluent api, which is very verbose and messy.

Don't create a generic repository interface. This will lead to have repositories with methods that shouldn't be there. Every repository should have only the methods needed as per your requirements.

Define the repository interfaces in your Business Layer. It's your business layer who dictates what a repository needs to implement, not the other way around. Then you can have one or multiple projects implementing these repositories, by referencing the business layer project(s), so they can map the model and implement the interfaces.

Do not consume your domain model from your UI layer. Your Business Layer should provide interfaces with write operations (commands), which accept input DTOs and with read operations (queries), which returns DTOs.

Define these interfaces and DTOs in a separate project owned by your Business Layer.Then your UI layer or web api controllers can reference that project and consume the interfaces.

If your business layer needs to access external services, define interfaces for them in the business layer, with the exact methods you need from those services. Then you can create projects implementing these interfaces. It's exactly the same concept as with the repositories.

3
  • It feels weird to have my DTOs interfaces in the domain, because that kinda creates a dependence on the UI. Suppose I have a clients lis page, and said page only need the name of a Client domain entity. Well, the UI is the one who knows about this, it is weird that I have to "ask the UI what it needs, to then implement it in the Domain so that the UI itself can consume it". From what I understand, the UI can reference both the DAL and Domain. In this case I would call GetClients from the repository, and map the Client object to a ClientViewModel, u in the UI not the Domain.
    – victor
    Feb 26, 2018 at 12:07
  • Your core doesn't depend on the UI. It's the other way around. Your core implements business requirements. If there is a requirement which is List all Products in a given Category, your Core will provide an interface with the GetProductsInCategory and the DTOs that it will return. If you consume this interface from WinForms, WebApi or unit test it's not a concern of your Core. And how the core implements this requirement, is not a concern of the UI either. You could replace the whole core implementation and your UI wouldn't care, because it only depends on the core interface. Feb 26, 2018 at 12:14
  • Note that you could have your core consumed by an MVC application and a WebApi (for mobile devices). Both clients would call the same core method, but one would map the DTO to a UI layer DTO with some extended information to pass to the razor view, while the other would have a different DTO (maybe with less information) to return in json to the mobile clients. Feb 26, 2018 at 12:25
0

(disclaimer, I'm not forcing or proposing this as a 1-off correct answer to your question, this is simply how I do it in my solutions, based upon the Onion architecture.)

I believe your main confusion comes from duplicating Domain objects or models in DAL layer. I always think of the Domain object as an entity in my backend database[*1]. To me this is the core piece of information I need persisted in order for the solution to function. Thus no need to replicate it the DAL layer.

This is how I structure my solution folder (based on your input):

  1. Domain Layer

    • Models that represent the DAL entity models database objects (tables and views).

    • Repositories interfaces.

  2. Data Access Layer

    • Db connection stuff.

    • Repositories implementations.

    • Models configuration (Fluent configuration).

  3. Infrastructure (Business) Layer

    • External services.

    • Utilities.

    • Specific services used later in front-end Layer.

  4. UI Application Layer

    • WebAPI project, sometimes in it's own folder for clearer separation[*2].

    • MVC/angular/react/etc application that uses Domain models all above layers as per Onion-architecture.

Each layer, top to bottom references everything from the previous layers. So in level 3. (Infrastructure) I reference the Domain and DAL layer projects, and so on.

[*1] I usually also include any database Views among Domain objects, this largely eliminates the need for a "UserService" just to make aggregates for the front-end layer.

[*2] Depending on the solution size/scope I omit the WebAPI project and simply consume all previous Layers directly in MVC app. If I however do use the WebAPI then the MVC app only uses this WebAPI and in some rare cases reference the Infrastructure layer's External services for example.

Edit as per comment:

The Infrastructure layer usually but not necessarily has these three projects:

  1. External services - let's say that my application needs data from a public DMV service. This is the place I would implement it in. Add a service reference, add logic for calling the DMV service, prepare Request and Response DTO objects which are then used to communicate with either the WebAPI or directly to MVC application.

  2. Utilities - this is one fairly big project of many static extensions or methods like for example "StringToDateTime" or "IsValidTaxNumber" etc. This one we strive to keep free from referencing other projects. I believe the only dependency this one currently has is Newtonsoft Json used in the Serialization section.

  3. Specific services - this is mainly used as a middleware to prevent my Controllers (API or MVC) from bloating. If I have a Controller Customer and there is some business logic I'll put it in this project. This way I have a clean speration of presentation and business/logic layers.

4
  • 1
    This is almost exactly what I had in mind, and indeed my confusion came from "a good design must have dal and domain entities, where domain entities are mapped to dal entites". Well, as it turns out it is just another case of bad literature, since there's no one shoe fits all solution. Nevertheless, I actually found a place where having a domain model different from the dal model makes sense: in my domain I have two models: user and account. In the DAL this is actually tepresented by a single table, thus a single dal entity, and I perform the mapping in the repository.
    – victor
    Feb 25, 2018 at 22:10
  • Im still a bit confused about "services". Can you provide some examples for each of #3 points?
    – victor
    Feb 25, 2018 at 22:17
  • @victor I made an edit to my post.
    – Iztoksson
    Feb 25, 2018 at 22:33
  • 2
    Domain should NOT equal the Db entities. Sep 23, 2018 at 12:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.