Doodad is an opinionated DDD and CQRS framework for .NET.
Work is based largely off of Microsoft's guide Tackling Business Complexity in a Microservice with DDD and CQRS Patterns and patterns used in the eShopOnContainers Ordering Service, with a little added flavour.
The DDD package contains the common base classes and interfaces referred to as the 'seedwork'. This term is used because it's usually just a small set of classes and tends to be copied and pasted between projects, but in this case, the aim is to provide a more formal structure.
- IEntity: An object defined by its identity.
- IAggregateRoot: The root object of an aggregate; a collection of objects treated as one unit.
- IDomainEvent: Something that happened that is of interest to domain experts.
- ValueObject: An immutable object defined by its values, without a concept of identity.
- Enumeration: A set of possible named values.
- Singleton: A unit type that only allows one value.
- IRepository: Retreives an aggregate from a storage.
public class Person : Entity, IAggregateRoot
{
public string Name { get; set; }
}
ValueObjects can often be mathematical values. The package provides ways to remove boilerplate when implementing C# operators to add, subtract, multiply and divide these values.
- IProduct: A value derived from multiplying two factors.
- IQuotient: A value derived from dividing a dividend by a divisor.
public class Force : ProductValueObject<Force, double, Mass, Acceleration>
{
public Force(double value) : base(value) { }
protected override IEnumerable<object> GetValues()
{
yield return Value;
}
}
The CQRS package brings in a dependency on MediatR and defines interfaces to better separate commands and queries.
- ICommand: Changes the state of a system but does not return a value.
- IQuery: Returns results and does not change the state of the system.
- IEvent: Something that has happened in the system.
public class DoTheThingCommandHandler : ICommandHandler<DoThingCommand>
{
public Task<CommandResult> Handle(DoThingCommand command)
{
}
}
MediatR notification handlers are their own classes, but sometimes we want a way to subscribe and unsubscribe at runtime.
- IMediatorSubscriber: Allows subscribing to an event at runtime.
internal class WorkMonitor
{
public WorkMonitor(IMediatorSubscriber subscriber)
{
subscriber.Subscribe<WorkDoneEvent>(HandleWorkDone);
}
private Task HandleWorkDone(WorkDoneEvent workDoneEvent)
{
}
}