Centeva.DomainModeling.Testing
8.0.0
Prefix Reserved
See the version list below for details.
dotnet add package Centeva.DomainModeling.Testing --version 8.0.0
NuGet\Install-Package Centeva.DomainModeling.Testing -Version 8.0.0
<PackageReference Include="Centeva.DomainModeling.Testing" Version="8.0.0" />
paket add Centeva.DomainModeling.Testing --version 8.0.0
#r "nuget: Centeva.DomainModeling.Testing, 8.0.0"
// Install Centeva.DomainModeling.Testing as a Cake Addin #addin nuget:?package=Centeva.DomainModeling.Testing&version=8.0.0 // Install Centeva.DomainModeling.Testing as a Cake Tool #tool nuget:?package=Centeva.DomainModeling.Testing&version=8.0.0
Centeva.DomainModeling
This package contains types (classes and interfaces) for building a rich domain layer for your application.
Built With
Getting Started
Add a reference to Centeva.DomainModeling
in your project.
You should only need to reference this package from the lowest layer of your solution. If you are using multiple projects to separate Core/Domain, Application, and Web API layers (i.e., "Clean" or "Ports and Adapters" architecture) then reference from the Core project.
Using Entity Framework Core
Reference the Centeva.DomainModeling.EFCore
package to get an implementation
of the Repository pattern for this ORM. Reference this package from your
Infrastructure project if your solution separates concerns by project.
This requires EF Core and MediatR to be added to your application's services configuration for dependency injection. See the documentation for these tools individually for instructions.
Using AutoMapper for Projection Support
You can use AutoMapper to provide projection support for your Repositories, which will allow you to map your entities to another type and only request the data you need from the database.
Import the Centeva.DomainModeling.EFCore.AutoMapper
package, add AutoMapper to
your application's services configuration for dependency injection, and derive
your Repository classes from BaseProjectedRepository
. See the AutoMapper
documentaion
for information on dependency injection setup.
Technical Patterns
Entities
An entity is a plain object that has a unique identifier and contains properties that are likely independently mutable. Two instances of an entity type that have the same identifier should be considered to be equivalent.
In most cases your project will involve persisting entities to some kind of data
storage, such as an SQL database. However, the details of such persistence
should not be contained within the definitions of those entities. (Avoid things
like Entity Framework annotation attributes like [Table]
.)
The BaseEntity
class can be inherited for your project's entities.
- The
Id
property (your entity's unique identifier) has apublic
setter but try to avoid using it in application code, especially if your database is auto-generating values. However, it can be helpful when seeding data both in tests and in your application.
You should strive to protect an entity's invariants using appropriate measures such as private setters, read-only collections, constructors, and public methods for updating the entity's properties.
In addition, register Domain Events within your entity's methods if you expect to have side effects as a result of calling those methods (such as changes to other unconnected entities.)
Value Objects
A value object represents something in your domain that has no unique identity. A value object is ideally immutable and equality is determined by comparing its properties.
Entities can (and should) contain value objects, but value objects should never contain entities.
Your value object classes should inherit from the ValueObject
class to gain
equality functionality.
See https://enterprisecraftsmanship.com/posts/value-objects-explained/ for more information about this concept.
Aggregates
An Aggregate is a collection of domain objects (Entities and Value Objects) that
is treated as a single unit for manipulation and enforcement of invariants
(validation rules). A good example is an Order
with its collection of
OrderItem
s.
One of the entities in an aggregate is the main entity or "root" and holds references to the other ones. An aggregate is retrieved via its root and outside entities should strive to reference other aggregates only via the root.
You can use the IAggregateRoot
interface to mark the roots of your aggregates.
This is just a marker interface (no properties or methods) and it's up to you to
enforce the Aggregate pattern. (See below for information about enforicing in
your repositories.)
Domain Events
Each entity inheriting from BaseEntity
contains a DomainEvents
list which
you can use for storing and later publishing Domain Events. These are used to
implement "side effects" based on work done to your entities. These events can
be handled elsewhere in your code to better decouple them from other parts of
the system. You will need to use the DomainEventDispatcher
in your
application to publish and handle these, likely inside of your Entity Framework
DbContext
.
Repositories
Repository is a pattern used to control and constrain access to data. It
defines standard CRUD operations on a set of entities of the same type.
Read-only operations are defined in IReadRepository
while IRepository
adds
update operations to those. This not only better adheres to the Interface
Segregation Principle, but allows implementers to add features such as caching
that would only apply to read operations.
If you are implementing Aggregates, your repositories should only operate on the root of each Aggregate, as child entities should never be directly accessed.
The package Centeva.DomainModeling.EFCore
provides an abstract implementation
of IRepository
named BaseRepository
. You can use it by creating a derived
class in your project. Additionally, if you want to enforce that repositories
can only access aggregate roots, then your derived class should look like this:
public class EfRepository<T> : BaseRepository<T>, IRepository<T> where T : class, IAggregateRoot
{
public EfRepository(ApplicationDbContext dbContext)
: base(dbContext) { }
}
If you are using the AutoMapper project feature, then derive your repository
from the BaseProjectedRepository
class like this:
public class EfRepository<T> : BaseProjectedRepository<T>, IRepository<T>, IProjectedReadRepository<T> where T : class, IAggregateRoot
{
public EfRepository(ApplicationDbContext dbContext, IConfigurationProvider mappingConfigurationProvider)
: base(dbContext, mappingConfigurationProvider) { }
}
Specifications
Specification is a pattern used to pull query logic out of other places in an
application and into self-contained, shareable, testable classes. This
eliminates the need to add custom query methods to your Repository, and avoids
other anti-patterns such as leaked IQueryable
objects.
See the documentation for the Ardalis.Specification library for more information and examples.
Running Tests
From Windows, use the dotnet test
command, or your Visual Studio Test
Explorer. Integration tests will use SQL Server LocalDB.
To run tests in a set of containers with Docker Compose, which is useful for
build pipelines and non-Windows development environments, use the
ci/run-tests.sh
or ci/run-tests.bat
scripts.
Deployment
Use dotnet pack
to generate a NuGet package. This library is versioned by
GitVersion. Create a Git tag for an official release
(e.g., "v1.0.0").
Contributing
Please use a Pull Request to suggest changes to this library. You should not add any functionality or dependency that is not appropriate for use at the lowest level (the "domain" level) of an application.
Resources
Take a look at https://bitbucket.org/centeva/centeva.templates for more implementation details.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net6.0
- Centeva.DomainModeling (>= 8.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
10.1.0 | 133 | 9/5/2024 |
10.1.0-tags-v10-1-0-pre-1.1 | 51 | 9/5/2024 |
10.0.0 | 105 | 9/5/2024 |
9.0.0-pre.1 | 127 | 12/29/2023 |
8.1.0 | 299 | 12/18/2023 |
8.0.0 | 197 | 10/30/2023 |
7.0.0 | 140 | 10/30/2023 |