The Hybrid DDD Architecture is organized into three distinct layers, each with specific responsibilities and dependencies. This layered architecture ensures separation of concerns, maintainability, and testability.
The Domain Layer contains the core business logic and is the heart of the application. It is framework-agnostic and has no dependencies on infrastructure concerns.
Rich domain objects that encapsulate business logic and maintain invariants.
public class DummyEntity : DomainEntity<string, DummyEntityValidator>{ /// <summary> /// Properties have private setters to restrict modifications /// from the Application layer /// </summary> public string DummyPropertyOne { get; private set; } public DummyValues DummyPropertyTwo { get; private set; } public DummyEntity(string dummyPropertyOne, DummyValues dummyPropertyTwo) { Id = Guid.NewGuid().ToString(); SetdummyPropertyOne(dummyPropertyOne); DummyPropertyTwo = dummyPropertyTwo; } public void SetdummyPropertyOne(string value) { DummyPropertyOne = value ?? throw new ArgumentNullException(nameof(value)); } public void SetdummyPropertyTwo(DummyValues value) { DummyPropertyTwo = value; }}
Source: Domain/Entities/DummyEntity.cs:12-47
Key Design Principle: Domain entity properties have private setters. This restricts modifications and ensures that changes only occur through domain methods that enforce business rules.
FluentValidation-based validators that define business rules.
public class DummyEntityValidator : EntityValidator<DummyEntity>{ public DummyEntityValidator() { // Business rules are defined here RuleFor(x => x.DummyPropertyOne) .NotNull() .NotEmpty() .WithMessage(DomainConstants.NOTNULL_OR_EMPTY); }}
// Domain constantspublic static class DomainConstants{ public const string NOTNULL_OR_EMPTY = "Value cannot be null or empty";}// Domain enumspublic static class Enums{ public enum DummyValues { Value1, Value2, Value3 }}
The Application Layer orchestrates domain logic to implement use cases. It defines interfaces for infrastructure concerns and coordinates the flow of data.
Commands represent write operations that change system state.
public class CreateDummyEntityCommand : IRequestCommand<string>{ [Required] public string dummyPropertyOne { get; set; } public DummyValues dummyPropertyTwo { get; set; } public CreateDummyEntityCommand() { }}
Simple objects for transferring data across boundaries.
public class DummyEntityDto{ public string Id { get; set; } public string DummyPropertyOne { get; set; } public DummyValues DummyPropertyTwo { get; set; }}
public class Mapping : Profile{ public Mapping() { CreateMap<DummyEntity, DummyEntityCreated>().ReverseMap(); CreateMap<DummyEntity, DummyEntityUpdated>().ReverseMap(); CreateMap<DummyEntity, DummyEntityDto>().ReverseMap(); }}
The Infrastructure Layer provides concrete implementations of interfaces defined in the Application layer. It handles technical concerns like persistence, messaging, and external integrations.
internal sealed class DummyEntityRepository : BaseRepository<DummyEntity>, IDummyEntityRepository{ public DummyEntityRepository(StoreDbContext context) : base(context) { }}
Source: Infrastructure/Repositories/Mongo/DummyEntityRepository.cs:13-17The MongoDB repository inherits from BaseRepository<TEntity> which is provided by the Core.Infrastructure.Repositories.MongoDb module.SQL Server Implementation:
internal sealed class DummyEntityRepository : BaseRepository<DummyEntity>, IDummyEntityRepository{ public DummyEntityRepository(StoreDbContext context) : base(context) { }}
The SQL Server repository uses Entity Framework Core for data access.
public class DatabaseFactory{ public static IMongoDatabase CreateMongoDatabase(string connectionString, string databaseName) { var client = new MongoClient(connectionString); return client.GetDatabase(databaseName); }}