RO EN
DDD Architecture in .NET Core: How to structure backend projects in a clean and scalable way
Doru Bulubasa
28 May 2025

🔰 1. What is Domain-Driven Design (DDD)?

  1. Invented by Eric Evans, focused on domain-centered models.
  2. Separates code into the experts' language (Ubiquitous Language).
  3. Major advantages:

  • Code that is easy to test
  • Clear separation of responsibilities
  • Long-term logical scalability

📐 2. Structuring the .NET Core solution into layers (projects)

Recommended organization of the solution:
/src
  ├── Blog.Domain           <- models, entities, Value Objects
  ├── Blog.Application      <- application services, commands, DTOs
  ├── Blog.Infrastructure   <- EF Core, repositories, external deps
  └── Blog.API              <- Web API Controllers, DI, presentation
Explanation of each layer:
    • Domain: pure logic, without external dependencies
    • Application: orchestration between services, no direct DB access
    • Infrastructure: technical details (SQL, Redis, Email)
    • API: controllers, authentication, external DTOs

🧱 3. Domain model – Creating the Post aggregate

public class Post : Entity<Guid>
{
    public Title Title { get; private set; }
    public Slug Slug { get; private set; }
    public PostContent Content { get; private set; }
    public DateTime PublishedAt { get; private set; }

    public Post(Title title, Slug slug, PostContent content)
    {
        Title = title;
        Slug = slug;
        Content = content;
        PublishedAt = DateTime.UtcNow;
    }
}

Value Object: Slug

public class Slug : ValueObject
{
    public string Value { get; }

    private Slug(string value)
    {
        Value = value;
    }

    public static Slug FromTitle(string title)
    {
        var slug = Regex.Replace(title.ToLower(), @"[^a-z0-9]+", "-");
        return new Slug(slug.Trim('-'));
    }

    protected override IEnumerable<object> GetEqualityComponents()
    {
        yield return Value;
    }
}

📦 4. Repository Pattern + Unit of Work

Generic interface:
public interface IRepository<T> where T : class
{
    Task<T?> GetByIdAsync(Guid id);
    Task AddAsync(T entity);
    void Remove(T entity);
}

Unit of Work:
public interface IUnitOfWork
{
    Task<int> SaveChangesAsync();
}

In Infrastructure, we will implement PostRepository with EF Core.


💡 5. Application Layer + MediatR

Create post command:

public record CreatePostCommand(string Title, string Content) : IRequest<Guid>;

Handler:

public class CreatePostHandler : IRequestHandler<CreatePostCommand, Guid>
{
    private readonly IPostRepository _postRepo;
    private readonly IUnitOfWork _uow;

    public CreatePostHandler(IPostRepository postRepo, IUnitOfWork uow)
    {
        _postRepo = postRepo;
        _uow = uow;
    }

    public async Task<Guid> Handle(CreatePostCommand request, CancellationToken ct)
    {
        var slug = Slug.FromTitle(request.Title);
        var post = new Post(new Title(request.Title), slug, new PostContent(request.Content));
        await _postRepo.AddAsync(post);
        await _uow.SaveChangesAsync();
        return post.Id;
    }
}

🔚 Conclusion

This article marks the beginning of implementing our backend using DDD in .NET Core. We discussed key concepts, project structure, and implemented a first aggregate (Post) with robust patterns.

Distribuie: