Kf.Essentials.CleanArchitecture
are helper classes, structures and extension methods used to form a base for pragmatic, clean architecture minded design.
You can download/get it on NuGet
The classes/tools available are:
We draw a lot from the original Domain Driven Design guidelines here, and implement it in a pragmatic way. If you do not agree with this setup, there is flexibility to alter it when implementing, or else, simply ignore it. Do feel free to post an issue if something might be broken.
If you want some reading on what an entity and a value object represents, please read this blgpost Entity vs Value Object: the ultimate list of differences.
(see implementation)
Abstract generic class giving you the benefit of not writing boilerplate for an entity base class. TId
stands for the type of the id you use in your domain model. It is recommended when started your own project not just to implement directly from Entity<TId>
, but to create your own implementation based on it. E.g.:
public abstract class DomainEntity : Entity<long>
{
protected DomainEntity(long id)
: base(id)
{ }
}
public sealed class Person : DomainEntity
{
// rest of the implementation here...
}
Furthermore if you use a non-native type for your id, please consider overriding the CompareId
method as this is implemented in the Equals
method. Overriding it means you can apply specific logic for your id type, or apply other domain specific logic determining the equality between two id's.
public abstract class DomainEntity : Entity<long>
{
protected DomainEntity(long id)
: base(id)
{ }
protected override bool CompareId(long a, long b)
{
// implement your custom compare logic between a and b
}
}
Last of all, I refer to the TestDomain for example usage.
(see implementation)
Abstract class that provides you the benefit of not writing boilerplate for a valueobject base class.
When inheriting from in you will need to override the property EquatableValues
, this is an IEnumerable<object>
that represents all properties, fields in the object that need to be taken into account when being compared, it essentially defines how Equals
and HashCode
are calculated and thus defines the "identity" of the object. Here's an example implementation.
public sealed class Name : ValueObject
{
// rest of the implementation here...
public string First { get; }
public string Last { get; }
protected override IEnumerable<object> EquatableValues
=> new[] { First, Last };
}
Last of all, I refer to the TestDomain for example usage.
Both the queries and commands are built on top of MediatR, this allows you to make sure your query or command has options to be plugged in to MediatR's pipeline behavior (and other MediatR support). However recommended for the benefits it is not necessary to use them with MediatR in mind, you can perfectly use them without needing MediatR if you assign the correct DI logic, see below for more information.
(see implementation) Set of interfaces and classes to provide an easy way to define a (reusable) query that can be handled by a queryhandler. I refer to the TestDomain for example usage.
For optimal use make sure to register your IQueryHandler<TQuery, TQueryResult>
with a DI container, either manually or by using an assembly scanner like Scrutor, if done so you could then register all IQueryHandler<,>
types like so to the IServiceCollection
:
public void ConfigureServices(IServiceCollection services)
{
// other services...
services.Scan(scan =>
scan
.FromAssembliesOf(typeof(SomeTypeWhereYourQueryHandlersAreLocated))
.AddClasses(classes => classes.AssignableTo(typeof(IQueryHandler<,>)))
.AsImplementedInterfaces()
.WithScopedLifetime()
);
}
(see implementation) Set of interfaces and classes to provide an easy way to define a command that can be handled by a commandhandler. I refer to the TestDomain for example usage.