CraftersCloud.ReferenceArchitecture.ProjectTemplates 2.1.5

There is a newer version of this package available.
See the version list below for details.
dotnet new install CraftersCloud.ReferenceArchitecture.ProjectTemplates::2.1.5                
This package contains a .NET Template Package you can call from the shell/command line.

Crafters Cloud Reference Architecture

CI NuGet NuGet MyGet (dev)

Overview

This project provides a reference for building scalable, high-performing, and maintainable applications using C#, SQL (SQL Server), and Distributed Cache (Redis).

It includes a set of Visual Studio project templates, published as a NuGet package, which can scaffold an entire solution with all the projects pre-configured via dotnet new crafters-starter, or scaffold a new feature using dotnet new crafters-feature.

The architecture leverages several modern technologies and best practices to ensure high performance, maintainability, and ease of development. It is based on the principles of Domain-Driven Design (DDD), CQRS, and Vertical Slice Architecture.

Primarily designed to be flexible and extensible, the architecture allows developers to quickly create new features (vertical slices) paired with corresponding integration tests (black-box tests that cover entire verticals). It does not prioritize either Onion Architecture or Clean Architecture, but both can be supported if and when more complex cases arise.

Usage

Prerequisites

  • .NET SDK 9
  • Aspire SDK
  • Docker

Getting Started

Either:
  1. Clone the Git repository

     git clone https://github.com/crafters-cloud/crafters-cloud-reference-architecture.git
    
Or:
  1. Install the Project Templates from NuGet

    dotnet new install CraftersCloud.ReferenceArchitecture.ProjectTemplates
    
  2. Create a New Solution Replace Client.Project and Client Project with the name of your new project.

    dotnet new crafters-starter --projectName Client.Project --friendlyName "Client Project" --allow-scripts yes
    
  3. Run new Solution and Verify

    • Start the Docker.
    • Open the new solution in Visual Studio or Rider.
    • Build the solution and run tests.
  4. Scaffold a New Feature

    • Either run the script scripts\new-feature.ps1 or
    • execute:
    dotnet new crafters-feature --projectName Client.Project --featureName Order --featureNamePlural Orders --allow-scripts yes
    

Solution Structure

The solution consists of a set of pre-configured and connected projects, grouped in three solution folders: src, templates, and tests.

src folder

Contains the following projects:

---
title: Project Structure
config:
  theme: default
---
flowchart BT
APP[Application]
INFRA[Infrastructure]
API[API]
MIG[Migrations]
MIG_SVC[MigrationService]
SVC_DEF[ServiceDefaults]
APP_HOST[AppHost]
DOMAIN[Domain]
CORE[Core]

DOMAIN --> CORE
APP --> DOMAIN
INFRA --> APP
API --> INFRA
API --> SVC_DEF
MIG --> INFRA
MIG_SVC --> MIG
MIG_SVC --> SVC_DEF
APP_HOST --> API
APP_HOST --> MIG_SVC
  • Core: Root project upon which all other projects depend. It should remain lightweight, containing only non-business-related code.
  • Domain: Contains the business rules (Domain Entities, Value Objects, Domain Services, Domain Validation rules/invariants).
  • Application: Business use case layer where Application Services or Query/Command Handlers are implemented.
  • Infrastructure: For "dirty" infrastructure-related code, such as API client implementations, Service Bus integrations, database provider implementations, logging, IoC container bootstrapping, cache implementations, etc.
  • Migrations: Contains database migration and seeding definitions.
  • MigrationService: A worker project for automatic database migrations, in combination with Aspire Host.
  • AppHost: Startup project to initialize dependent resources (e.g., SQL, Redis, API) using Microsoft Aspire.
  • Api: "Presentation" layer.
  • ServiceDefaults: Aspire Shared project.

templates folder

Contains project templates:

  • Feature: Template for adding a new feature.
  • Solution: Template for creating a new solution.

tests folder

Contains the following test projects:

  • Api.Tests: Tests for the Api project.
  • AppHost.Tests: Tests for the AppHost project.
  • Domain.Tests: Tests for the Domain layer.
  • Infrastructure.Tests: Tests for the Infrastructure layer.
  • Tests.Shared: Shared code for test projects.

Domain (and Entities)

All business rules should be defined in the Domain project. Other projects should re-use the business rules located in the Domain as much as possible. These business rules are defined through a set of classes: Entity, Value Object, Domain Events, Validation Rules, Business Rules, and extension methods on the domain classes.

  • Entity - an object defined primarily by its ID, and not by its properties:

    • An Entity is any class that inherits (directly or indirectly) from the Entity abstract class.
    • An Entity class (as defined in this architecture, which knowingly deviates slightly from pure DDD) serves two purposes:
      • It is used for mapping between a database table and a class (ORM mapping).
      • It encapsulates business rules to ensure domain invariants.
    • An Entity class should be designed to prevent invalid domain states (adhering to the Always-Valid Domain Model Approach) and disallow updates without raising domain events (critical for event dispatching). This is achieved by:
      • Making all setters private.
      • Exposing collections as readonly collections.
      • Allowing data updates only through explicitly defined public methods (to ensure that business rules are not violated and domain events are raised).
      • Making public methods internally raise domain events (private methods typically do not need to raise them, as they are invoked by public methods).
      • Making the default constructor private (used only by EF Core).
      • Providing public factory methods for creating entities in a valid state.
      • Using the Execute/CanExecute pattern for public methods requiring complex business rule validation:
        • CanExecute returns results (consuming code checks the result and only calls Execute if valid; otherwise, errors are propagated to the calling code).
        • Execute throws exceptions.
  • Value Object - an immutable type defined by its properties rather than a unique identity. C# "Records" are ideal for Value Objects. They are mainly used for Entity IDs but can also represent other concepts like names, emails, etc. Value Objects must:

    • Be immutable.
    • Be comparable based on their properties.
    • Always be valid (Value Objects should prevent creation in an invalid state).
    • Not have individual identity.
  • Domain Event - a set of classes that communicate changes occurring in an entity to its event subscribers, following the Publish/Subscribe pattern.

    • Domain events are stored in the entity instance using the AddDomainEvent method.
    • Domain events are collected and published after calling SaveChanges on the Entity Framework DbContext, but before the database transaction is committed. This allows additional changes to be made as part of the same transaction (e.g., adding integration events to the database).
  • QueryableExtension - a set of classes that extend the IQueryable<ConcreteEntity> interface, providing a simple implementation of the Specification pattern.

    • Instead of creating methods within IRepository<ConcreteEntity>, it is more flexible to use extension methods on IQueryable<ConcreteEntity> so that different queries can be chained or combined.
  • EnumerableExtension - a set of classes that extend the IEnumerable<ConcreteEntity> interface.

    • Filter expressions that cannot be translated by the concrete Queryable provider (e.g., Entity Framework) should be placed here. However, if they can be translated, they should belong to QueryableExtensions instead.
  • Validations - a set of classes that contain validation rules that can be used by either the entity (to validate itself) or by other layers (e.g. API for Request validation, Application for Command/Query validation). With the combination of Always-Valid Domain Model Approach the set of rules should remain simple.

Testing Strategy

Following the principles of Black Box testing, XP (eXtreme Programming), and YAGNI (You Aren't Gonna Need It), the focus is to first write integration tests that cover each feature (e.g., in the case of an API, testing every API endpoint) and cover a happy-flow path. These tests require minimal setup (no fakes, mocks, or stubs) but only database data seeding.
Later, as the code complexity grows in the domain area (business rules), it is recommended to write unit tests for the domain. With this combination of unit and integration tests, we should achieve a Testing Diamond structure, where the optimum number of tests is needed to achieve optimum test coverage.

The tests adhere to Black Box testing principles of API endpoints. This approach allows the implementation and architecture of the system to evolve without requiring test updates.

White-box testing (using fakes and mocks) is still possible but is generally considered a code smell for the majority of tests. It can hinder code evolution and architectural changes, as both the implementation and the tests would require updates in tandem.

To verify test results effectively, the libraries VerifyTests and Shouldly are used, providing a more readable way of validating outcomes.

With this strategy, developers are encouraged and eager to write and maintain tests. The refactoring of both the architecture and the code over time ensures technical debt is kept under control.
Arbitrary decisions regarding test coverage (e.g., endpoints, services, domain entities) are avoided, reducing team ambiguity and friction while simplifying pull requests.

Unit tests should be written for complex business rules. However, given that this is just a sample project and there are no complex business rules, it does not include a good example of a unit test.

Key Technologies

  • Minimal API: A lightweight, efficient framework for building RESTful services.
  • Autofac: A popular IoC container for managing application dependencies.
  • Entity Framework: An O/RM enabling database operations via .NET objects.
  • MediatR: Simplifies decoupling of requests and handlers.
  • Mapperly: A fast object-to-object mapper for transforming objects between layers.
  • FluentValidation: Strongly typed model validation rules.
  • NUnit: A flexible unit testing framework for .NET.
  • TestContainers: An open source library for providing throwaway, lightweight instances of databases, message brokers, web browsers, or just about anything that can run in a Docker container.
  • Microsoft Aspire: A set of tools, templates, and packages for building observable, production ready apps.

Together, these technologies create a strong foundation for developing modern, high-quality applications.

How-tos

This package has no dependencies.

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
2.1.7 27 2/21/2025
2.1.7-preview.4 22 2/21/2025
2.1.7-preview.3 20 2/21/2025
2.1.7-preview.2 26 2/21/2025
2.1.7-preview.1 27 2/21/2025
2.1.6 24 2/21/2025
2.1.5 26 2/21/2025
2.1.4 30 2/21/2025
2.1.3 24 2/21/2025
2.1.2 29 2/21/2025
2.1.1-preview.0.1 46 2/11/2025
2.1.0 78 2/11/2025
2.1.0-preview.2 38 2/10/2025
2.1.0-preview.1 35 1/22/2025
2.1.0-preview.0 37 1/22/2025
2.0.3 60 2/6/2025
2.0.2 72 1/22/2025
2.0.1 67 1/22/2025
2.0.0 70 1/21/2025
2.0.0-preview.6.2 30 1/21/2025
2.0.0-preview.6.1 32 1/21/2025
2.0.0-preview.5 32 1/17/2025
2.0.0-preview.0 27 1/17/2025
1.2.0 76 1/17/2025
1.1.1 75 1/15/2025
1.1.0 69 1/15/2025
0.3.1 65 1/14/2025
0.3.0 59 1/14/2025