Skip to main content

Introduction

The Hybrid DDD Architecture includes several reusable core modules that provide common infrastructure functionality. These modules can be shared across multiple projects and provide consistent patterns for CQRS, event handling, persistence, and more.

Module Overview

The core modules are organized by responsibility:

Command/Query Bus

CQRS implementation using MediatR

Event Bus

Event publishing and subscription abstractions

Repositories

Generic repository interfaces for data access

Mapping

Object-to-object mapping utilities

HTTP Adapters

HTTP client abstractions for external APIs

Domain

Base entity classes and validation

Core.Application.CommandQueryBus

Provides abstractions and implementations for the CQRS pattern using MediatR.

Key Components

ICommandQueryBus

The main bus interface for sending commands, queries, and publishing notifications.
public interface ICommandQueryBus
{
    Task Publish<TNotification>(TNotification notification, CancellationToken cancellationToken = default) 
        where TNotification : INotification;
    
    Task Send(IRequest request, CancellationToken cancellationToken = default);
    
    Task<TResponse> Send<TResponse>(IRequest<TResponse> request, CancellationToken cancellationToken = default);
}

Command Interfaces

Define command contracts for write operations.
public interface IRequestCommand : IRequest
{
}

public interface IRequestCommand<out TResponse> : IRequest<TResponse>
{
}

Query Interfaces

Define query contracts for read operations.
public interface IRequestQuery : IRequest
{
}

public interface IRequestQuery<out TResponse> : IRequest<TResponse>
{
}

Handler Interfaces

Define handler contracts for processing commands and queries.
public interface IRequestCommandHandler<in TRequest> : IRequestHandler<TRequest>
    where TRequest : IRequest
{
}

public interface IRequestCommandHandler<TRequest, TResponse> : IRequestHandler<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
{
}
Source: Core.Application.CommandQueryBus/Commands/IRequestCommandHandler.cs:5-13

Notifications and Domain Events

Support for domain event notifications.
public class DomainEvent : IRequestNotification
{
    public DateTime EventDateUtc { get; private set; }

    public DomainEvent()
    {
        EventDateUtc = DateTime.UtcNow;
    }
}
Source: Core.Application.CommandQueryBus/Notifications/DomainEvent.cs:3-10

QueryRequest and QueryResult

Base classes for paginated queries.
public class QueryRequest<TResponse> : IRequestQuery<TResponse>
    where TResponse : class
{
    public uint PageIndex { get; set; }
    public uint PageSize { get; set; }
}
Source: Core.Application.CommandQueryBus/Queries/QueryRequest.cs:3-8
public class QueryResult<TEntity>
    where TEntity : class
{
    public long Count { get; private set; }
    public IEnumerable<TEntity> Items { get; private set; }
    public uint PageIndex { get; private set; }
    public uint PageSize { get; private set; }

    public QueryResult(IEnumerable<TEntity> items, long count, uint pageSize, uint pageIndex)
    {
        Items = items;
        Count = count;
        PageIndex = pageIndex;
        PageSize = pageSize;
    }
}
Source: Core.Application.CommandQueryBus/Queries/QueryResult.cs:3-17
The QueryResult class provides built-in pagination support for query responses.

Core.Application.EventBus

Provides abstractions for event-driven architecture with support for integration events.

Key Components

IEventBus

The main event bus interface for publishing and subscribing to integration events.
public interface IEventBus
{
    void Publish(IntegrationEvent @event);

    void Subscribe<TEvent, THandler>()
        where TEvent : IntegrationEvent
        where THandler : IIntegrationEventHandler<TEvent>;

    void SubscribeDynamic<THandler>(string eventName)
        where THandler : IDynamicIntegrationEventHandler;

    void Unsubscribe<TEvent, THandler>()
        where TEvent : IntegrationEvent
        where THandler : IIntegrationEventHandler<TEvent>;

    void UnsubscribeDynamic<THandler>(string eventName)
        where THandler : IDynamicIntegrationEventHandler;
}
Source: Core.Application.EventBus/Buses/IEventBus.cs:3-20

IntegrationEvent

Base class for all integration events.
public abstract class IntegrationEvent
{
    [JsonProperty]
    public Guid Id { get; private set; }

    [JsonProperty]
    public DateTime CreateDateUtc { get; private set; }

    [JsonProperty]
    public string EventType { get; private set; }

    [JsonProperty]
    public string Subject { get; private set; }

    [JsonProperty]
    public object Data { get; private set; }

    protected IntegrationEvent(string eventType, string subject, object data)
    {
        Id = Guid.NewGuid();
        CreateDateUtc = DateTime.UtcNow.ToLocalTime();
        EventType = eventType;
        Subject = subject;
        Data = data;
    }
}
Source: Core.Application.EventBus/Events/IntegrationEvent.cs:5-35

Infrastructure Implementation

The template includes a RabbitMQ implementation:
  • Core.Infrastructure.EventBus.RabbitMq: RabbitMQ-based event bus implementation
  • Supports publish/subscribe patterns
  • Includes retry policies and connection management
  • Handles dynamic and typed event subscriptions
Source: Core.Infrastructure.EventBus.RabbitMq/Buses/RabbitMqEventBus.cs
The event bus requires proper configuration of RabbitMQ connection settings in your application.

Core.Application.Repositories

Provides generic repository abstractions for data access.

IRepository Interface

public interface IRepository<TEntity>
{
    object Add(TEntity entity);
    Task<object> AddAsync(TEntity entity);
    
    long Count(Expression<Func<TEntity, bool>> filter);
    Task<long> CountAsync(Expression<Func<TEntity, bool>> filter);
    
    List<TEntity> FindAll();
    Task<List<TEntity>> FindAllAsync();
    
    TEntity FindOne(params object[] keyValues);
    Task<TEntity> FindOneAsync(params object[] keyValues);
    
    void Remove(params object[] keyValues);
    void Update(object id, TEntity entity);
}
Source: Core.Application.Repositories/IRepository.cs:5-17

Infrastructure Implementations

The template includes two repository implementations:

MongoDB Repository

public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
    public DbContext Context { get; private set; }
    public IMongoCollection<TEntity> Collection { get; private set; }

    public BaseRepository(DbContext context)
    {
        Context = context;
        Collection = Context.GetCollection<TEntity>();
    }

    public async Task<object> AddAsync(TEntity entity)
    {
        await Collection.InsertOneAsync(entity);
        var property = typeof(TEntity).GetProperty("Id") ?? typeof(TEntity).GetProperty("_id");
        return (object)property?.GetValue(entity);
    }
    
    // ... other methods
}
Source: Core.Infrastructure.Repositories.MongoDb/BaseRepository.cs:7-31

SQL Server Repository

  • Core.Infrastructure.Repositories.Sql: Entity Framework Core-based implementation
  • Supports SQL Server with migrations
  • Includes DbContext configuration
You can easily switch between MongoDB and SQL Server by changing your dependency injection configuration.

Core.Application.Mapping

Provides utilities for object-to-object mapping using AutoMapper.

CustomMapper

Extension methods for convenient mapping.
public static class CustomMapper
{
    public static IMapper Instance { get; set; }

    public static T To<T>(this object input)
    {
        IMapper mapper = Instance;
        return mapper.Map<T>(input);
    }

    public static T To<T>(this IValidate input)
    {
        IMapper mapper = Instance;
        return mapper.Map<T>(input);
    }

    public static IEnumerable<T> To<T>(this IEnumerable<IValidate> input)
    {
        IMapper mapper = Instance;
        return mapper.Map<IEnumerable<T>>(input);
    }
}
Source: Core.Application.Mapping/CustomMapper.cs:6-28

Usage Example

// Map entity to DTO
DummyEntityDto dto = entity.To<DummyEntityDto>();

// Map collection
IEnumerable<DummyEntityDto> dtos = entities.To<DummyEntityDto>();

Core.Application.Adapters.Http

Provides abstractions for HTTP client operations.

IExternalApiClient

public interface IExternalApiClient
{
    Task<string> GetAsync(string url, Tuple<string, string>? authentication = null, 
        IDictionary<string, string>? headers = null);
    
    Task<HttpResponseMessage> PostAsync<T>(string uri, T item, 
        Tuple<string, string>? authentication = null, IDictionary<string, string>? headers = null);
    
    Task<HttpResponseMessage> PutAsync<T>(string url, T item, 
        Tuple<string, string>? authentication = null, IDictionary<string, string>? headers = null);
    
    Task<HttpResponseMessage> DeleteAsync(string url, 
        Tuple<string, string>? authentication = null, IDictionary<string, string>? headers = null);
}
Source: Core.Application.Adapters.Http/IExternalApiClient.cs:3-9

Features

  • Supports authentication headers
  • Custom header injection
  • RESTful operations (GET, POST, PUT, DELETE)
  • Async/await pattern
The HTTP adapter provides a consistent interface for all external API calls, making it easy to mock in tests.

Core.Domain

Provides base classes for domain entities with built-in validation.

DomainEntity

Base class for all domain entities.
public class DomainEntity<TKey, TValidator> : IValidate
    where TValidator : IValidator, new()
{
    public TKey Id { get; protected set; }
    
    public bool IsValid
    {
        get
        {
            Validate();
            return ValidationResult.IsValid;
        }
    }

    protected TValidator Validator { get; }
    private ValidationResult ValidationResult { get; set; }

    protected DomainEntity()
    {
        Validator = new TValidator();
    }

    protected void Validate()
    {
        var context = new ValidationContext<object>(this);
        ValidationResult = Validator.Validate(context);
    }

    public IList<ValidationFailure> GetErrors()
    {
        Validate();
        return ValidationResult.Errors;
    }
}
Source: Core.Domain/Entities/DomainEntity.cs:13-44

Features

  • Generic key type support
  • Integrated FluentValidation
  • Self-validating entities
  • Immutable validation results

Usage Example

public class DummyEntity : DomainEntity<string, DummyEntityValidator>
{
    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));
    }
}
Source: Domain/Entities/DummyEntity.cs:12-42
Domain entity properties should have private setters to enforce invariants through methods.

Module Dependencies

The core modules have the following dependency structure:
Core.Domain (no dependencies)

Core.Application.* (depends on Core.Domain)

Core.Infrastructure.* (depends on Core.Application.*)
This structure ensures that:
  • Domain logic remains pure and independent
  • Application abstractions are framework-agnostic
  • Infrastructure provides concrete implementations

Next Steps

Architecture Layers

Learn how layers use these core modules

CQRS Pattern

See CQRS implementation in action

Domain Layer

Create your first domain entity

Repositories

Implement data persistence

Build docs developers (and LLMs) love