C# – DDD Best practices

cdomain-driven-designobject-oriented

I'm developing some test web-project for my self to learn DDD and good architectural practice at all. So application, basically, is a simple photo manager.

I'm developing 3-tier architecture and for now I have:

  • DAL:
    • EF database first
    • UserRepository
    • PhotoRespoitory
    • UnitOfWork
  • BLL

    • UserService: user registration, authentication, registration
    • PhotoService: photo retrieving, uploading, updating
    • DTO Models: User, Photo, Album
    • Automapper: mapping DAO from EF to DTO model
  • Asp.Net MVC Client

Here are examples of DTO models:

public abstract class ModelBase
{
    public virtual long Id { get; protected set; }
}

public class User : ModelBase
{
    public string Name { get; set; }
    public string Email { get; set; }
    public UserRole UserRole { get; set; }
    public DateTime RegistrationDate { get; set; }

    public override string ToString()
    {
        return $"{Name} {Email} {UserRole} {RegistrationDate}";
    }
}


public class Photo : ModelBase
{
    public string Name { get; set; }
    public DateTime DateTaken { get; set; }
    public string Place { get; set; }
    public string CameraModel { get; set; }
    public string FocalLength { get; set; }
    public string Diaphragm { get; set; }
    public string ShutterSpeed { get; set; }
    public string ISO { get; set; }
    public bool? FlashMode { get; set; }
    public long OwnerId { get; set; }
    public string OwnerName { get; set; }
    public long AlbumId { get; set; }
    public string AlbumName { get; set; }
}

So my questions are:

  1. Does it make sense to create domain models with some logic, as model manipulation logic was delegated to service?

  2. Is BLL the right place for DTO models?

  3. Do I need to create an additional ApplicationService that will contain PhotoService and PhotoService and finds user that uploads picture and then pass the UserDto with PhotoDto to PhotoService?

Best Answer

1) Does it make sense to create domain models with some logic, as model manipulation logic was delegated to service?

You haven't really specified what you mean by "domain model". This definition is important as some developers consider their database entities as their domain model, whereas others keep a separate domain project which contains the interfaces and base classes that are inherited/implemented by actual DAL/BLL layers.

  • If you refer to the DTO classes, you can add logic to them.
  • If you refer to the database entities, you shouldn't add logic to them. At best, you can make a compromise and allow e.g. a simple property conversion (public string FullName => FirstName + LastName;) but no actual logic.
  • If you refer to the interfaces/base classes for DAL/BLL objects, then it can be meaningful to add some base logic, though you need to ensure that is it is dependency-agnostic. More often than not, the logic you intend to add won't be dependency-agnostic.

This question is a bit too open-ended for a direct answer. Some developers maintain an anemic model where data classes and logic classes are strictly separate. Other developers argue that this is against OOP principles.

I'm not making a decision here pro/anti anemic. Either approach has its benefist. And, if we're being honest, the majority of developers are expected to follow their tech lead/senior's preferred approach anyway.

2) Is BLL the right place for DTO models?

Technically, every layer needs its own DTOs. But I think most people agree that the effort to do so does not outweigh the benefits.

If you only have one DTO layer, the business layer is the place to put them. Essentially, the rule of thumb is that your database entities do not leak outside of your BLL.

3) Do I need to create an additional ApplicationService, that will contain PhotoService and PhotoService and finds user that uploads picture and then pass the UserDto with PhotoDto to PhotoService?

I wouldn't call it ApplicationService. But it can be meaningful to have a service named after a function rather than an entity.

For example, the UserService handles user CRUD, the RoleService handles role CRUD, but the AuthorizationService handles the combination of the two: logins, resolving permissions, ... And it reuses logic from UserService/RoleService where relevant.

It would make little sense to put the login/permission logic in either the UserService or RoleService since it so heavily depends on both. Developers would disagree with its location or, even worse, start mixing them.

I think the shortest direct answer to your question is that services don't need to point to a particular database entity.