Hexalith.MyNewModule.Aggregates
1.2.0
dotnet add package Hexalith.MyNewModule.Aggregates --version 1.2.0
NuGet\Install-Package Hexalith.MyNewModule.Aggregates -Version 1.2.0
<PackageReference Include="Hexalith.MyNewModule.Aggregates" Version="1.2.0" />
<PackageVersion Include="Hexalith.MyNewModule.Aggregates" Version="1.2.0" />
<PackageReference Include="Hexalith.MyNewModule.Aggregates" />
paket add Hexalith.MyNewModule.Aggregates --version 1.2.0
#r "nuget: Hexalith.MyNewModule.Aggregates, 1.2.0"
#:package Hexalith.MyNewModule.Aggregates@1.2.0
#addin nuget:?package=Hexalith.MyNewModule.Aggregates&version=1.2.0
#tool nuget:?package=Hexalith.MyNewModule.Aggregates&version=1.2.0
Hexalith.MyNewModule
A comprehensive template repository for creating new Hexalith modules following Domain-Driven Design (DDD), CQRS (Command Query Responsibility Segregation), and Event Sourcing architectural patterns.
Build Status
Table of Contents
- Overview
- Architecture
- Prerequisites
- Getting Started
- Project Structure
- Domain Layer
- Application Layer
- Infrastructure Layer
- Presentation Layer
- Testing
- Configuration
- Running with .NET Aspire
- Development Workflow
- Contributing
- License
Overview
This repository provides a production-ready template for creating new Hexalith modules. It implements a clean architecture with clear separation of concerns across multiple layers:
- Domain Layer: Contains domain aggregates, events, and value objects
- Application Layer: Contains commands, command handlers, requests, and projections
- Infrastructure Layer: Contains API servers, web servers, and integration services
- Presentation Layer: Contains Blazor UI components and pages
The module follows CQRS and Event Sourcing patterns, using Dapr for distributed application runtime and Azure Cosmos DB for persistence.
Architecture
Architectural Patterns
┌─────────────────────────────────────────────────────────────────┐
│ Presentation Layer │
│ ┌─────────────────────┐ ┌──────────────────────────────────┐ │
│ │ UI.Components │ │ UI.Pages │ │
│ │ (Blazor Components)│ │ (Blazor Pages & Views) │ │
│ └─────────────────────┘ └──────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Infrastructure Layer │
│ ┌─────────────────┐ ┌──────────────┐ ┌─────────────────────┐ │
│ │ ApiServer │ │ WebServer │ │ WebApp │ │
│ │ (REST API) │ │ (SSR Host) │ │ (WASM Client) │ │
│ └─────────────────┘ └──────────────┘ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Application Layer │
│ ┌────────────┐ ┌───────────┐ ┌───────────┐ ┌───────────────┐ │
│ │ Commands │ │ Requests │ │Projections│ │ Handlers │ │
│ │ │ │ (Queries) │ │ │ │ │ │
│ └────────────┘ └───────────┘ └───────────┘ └───────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Domain Layer │
│ ┌────────────┐ ┌───────────┐ ┌───────────┐ ┌───────────────┐ │
│ │ Aggregates │ │ Events │ │ Value │ │ Localizations │ │
│ │ │ │ │ │ Objects │ │ │ │
│ └────────────┘ └───────────┘ └───────────┘ └───────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Key Design Principles
- Domain-Driven Design (DDD): Domain logic is encapsulated in aggregates with clear boundaries
- CQRS: Commands (writes) and Queries (reads) are separated into different models
- Event Sourcing: State changes are captured as a sequence of events
- Clean Architecture: Dependencies flow inward, with the domain layer at the core
- Modular Design: Each module is self-contained and can be deployed independently
Prerequisites
Before getting started, ensure you have the following installed:
- .NET 9 SDK or later (currently targeting .NET 10.0)
- PowerShell 7 or later
- Git
- Docker (for running with Aspire)
- Dapr CLI (optional, for local development)
Optional Tools
- Visual Studio 2022 or VS Code
- Cursor (recommended for AI-assisted development)
- Azure CLI (for Azure deployments)
Getting Started
1. Clone or Use as Template
Option A: Use as GitHub Template
- Click "Use this template" on GitHub
- Create a new repository with your desired name
Option B: Clone the Repository
git clone https://github.com/Hexalith/Hexalith.MyNewModule.git YourModuleName
cd YourModuleName
2. Initialize Your Package
Run the initialization script to customize the template for your module:
./initialize.ps1 -PackageName "YourPackageName"
Example:
./initialize.ps1 -PackageName "Inventory"
This creates a module named Hexalith.Inventory.
The initialization script will:
- Replace all occurrences of
MyToDowith your package name - Rename directories and files containing
MyToDo - Initialize and update Git submodules (Hexalith.Builds and HexalithApp)
- Set up the project structure for your new module
3. Initialize Git Submodules
The template uses Git submodules for shared build configurations:
git submodule init
git submodule update
4. Build the Solution
dotnet restore
dotnet build
5. Run Tests
dotnet test
Project Structure
Hexalith.MyNewModule/
├── AspireHost/ # .NET Aspire orchestration
│ ├── Program.cs # Aspire app configuration
│ ├── Components/ # Aspire component configurations
│ └── appsettings.json # Application settings
│
├── src/ # Source code root
│ ├── HexalithMyNewModuleApiServerApplication.cs
│ ├── HexalithMyNewModuleWebAppApplication.cs
│ ├── HexalithMyNewModuleWebServerApplication.cs
│ │
│ └── libraries/ # NuGet package projects
│ │
│ ├── Application/ # Application layer
│ │ ├── Hexalith.MyNewModule/ # Main application logic
│ │ ├── Hexalith.MyNewModule.Abstractions/ # Interfaces & contracts
│ │ ├── Hexalith.MyNewModule.Commands/ # Command definitions
│ │ ├── Hexalith.MyNewModule.Projections/ # Read model projections
│ │ └── Hexalith.MyNewModule.Requests/ # Query requests
│ │
│ ├── Domain/ # Domain layer
│ │ ├── Hexalith.MyNewModule.Aggregates/ # Domain aggregates
│ │ ├── Hexalith.MyNewModule.Aggregates.Abstractions/# Domain helpers
│ │ ├── Hexalith.MyNewModule.Events/ # Domain events
│ │ └── Hexalith.MyNewModule.Localizations/ # Resource files
│ │
│ ├── Infrastructure/ # Infrastructure layer
│ │ ├── Hexalith.MyNewModule.ApiServer/ # REST API server
│ │ ├── Hexalith.MyNewModule.Servers/ # Server helpers
│ │ ├── Hexalith.MyNewModule.WebApp/ # WASM client module
│ │ └── Hexalith.MyNewModule.WebServer/ # SSR web server module
│ │
│ └── Presentation/ # Presentation layer
│ ├── Hexalith.MyNewModule.UI.Components/ # Blazor components
│ └── Hexalith.MyNewModule.UI.Pages/ # Blazor pages
│
├── test/ # Test projects
│ └── Hexalith.MyNewModule.Tests/ # Unit & integration tests
│
├── HexalithApp/ # Hexalith application (submodule)
├── Hexalith.Builds/ # Build configurations (submodule)
│
├── Directory.Build.props # MSBuild properties
├── Directory.Packages.props # Central package management
├── Hexalith.MyNewModule.sln # Solution file
└── initialize.ps1 # Initialization script
Domain Layer
The domain layer contains the core business logic and is framework-agnostic.
Aggregates
Aggregates are the core domain entities that encapsulate business rules and state changes.
Location: src/libraries/Domain/Hexalith.MyNewModule.Aggregates/
/// <summary>
/// Represents a mytodo aggregate.
/// </summary>
/// <param name="Id">The mytodo identifier.</param>
/// <param name="Name">The mytodo name.</param>
/// <param name="Comments">The mytodo description.</param>
/// <param name="Disabled">The mytodo disabled status.</param>
[DataContract]
public sealed record MyToDo(
[property: DataMember(Order = 1)] string Id,
[property: DataMember(Order = 2)] string Name,
[property: DataMember(Order = 3)] string? Comments,
[property: DataMember(Order = 7)] bool Disabled) : IDomainAggregate
{
public ApplyResult Apply(object domainEvent)
{
// Event handling logic
}
}
Key features:
- Implements
IDomainAggregateinterface - Uses C# records for immutability
- Primary constructors for clean initialization
Applymethod handles domain events and returns new state
Domain Events
Events represent facts that have happened in the domain.
Location: src/libraries/Domain/Hexalith.MyNewModule.Events/MyToDo/
Available events:
MyToDoAdded- When a new module is createdMyToDoDescriptionChanged- When name or description changesMyToDoDisabled- When the module is disabledMyToDoEnabled- When the module is enabled
/// <summary>
/// Event raised when a new mytodo is added.
/// </summary>
[PolymorphicSerialization]
public partial record MyToDoAdded(
string Id,
[property: DataMember(Order = 2)] string Name,
[property: DataMember(Order = 3)] string? Comments)
: MyToDoEvent(Id);
Value Objects
Value objects are immutable domain concepts with no identity.
Location: src/libraries/Domain/Hexalith.MyNewModule.Aggregates.Abstractions/ValueObjects/
Localizations
Resource files for internationalization (i18n) support.
Location: src/libraries/Domain/Hexalith.MyNewModule.Localizations/
Files:
MyToDo.resx- English (default)MyToDo.fr.resx- FrenchMyNewModuleMenu.resx- Menu labels
Application Layer
The application layer coordinates domain operations and implements use cases.
Abstractions
Location: src/libraries/Application/Hexalith.MyNewModule.Abstractions/
Key files:
IMyToDoModule.cs- Module interfaceIMyToDoService.cs- Service interfaceMyToDoPolicies.cs- Authorization policiesMyToDoRoles.cs- Security roles
/// <summary>
/// Defines the roles for MyToDo security.
/// </summary>
public static class MyToDoRoles
{
public const string Owner = nameof(MyNewModule) + nameof(Owner);
public const string Contributor = nameof(MyNewModule) + nameof(Contributor);
public const string Reader = nameof(MyNewModule) + nameof(Reader);
}
Commands
Commands represent intent to change the system state.
Location: src/libraries/Application/Hexalith.MyNewModule.Commands/MyToDo/
Available commands:
AddMyToDo- Create a new moduleChangeMyToDoDescription- Update name/descriptionDisableMyToDo- Disable a moduleEnableMyToDo- Enable a module
/// <summary>
/// Command to add a new mytodo.
/// </summary>
[PolymorphicSerialization]
public partial record AddMyToDo(
string Id,
[property: DataMember(Order = 2)] string Name,
[property: DataMember(Order = 3)] string? Comments)
: MyToDoCommand(Id);
Requests (Queries)
Requests represent queries for data retrieval.
Location: src/libraries/Application/Hexalith.MyNewModule.Requests/MyToDo/
Available requests:
GetMyToDoDetails- Get full details of a moduleGetMyToDoSummaries- Get list of module summariesGetMyToDoIds- Get list of all module IDsGetMyToDoExports- Export module data
View Models
View models for presenting data to the UI.
MyToDoDetailsViewModel- Full module detailsMyToDoSummaryViewModel- Summary for lists
Projections
Projections handle event processing to update read models.
Location: src/libraries/Application/Hexalith.MyNewModule.Projections/
public static class MyToDoProjectionHelper
{
public static IServiceCollection AddMyToDoProjectionHandlers(
this IServiceCollection services)
=> services
.AddScoped<IProjectionUpdateHandler<MyToDoAdded>,
MyToDoAddedOnSummaryProjectionHandler>()
.AddScoped<IProjectionUpdateHandler<MyToDoDescriptionChanged>,
MyToDoDescriptionChangedOnSummaryProjectionHandler>()
// ... more handlers
}
Infrastructure Layer
The infrastructure layer contains technical implementations and hosting concerns.
API Server Module
Location: src/libraries/Infrastructure/Hexalith.MyNewModule.ApiServer/
Provides:
- REST API controllers
- Dapr actor registrations
- Service registrations
- Integration event handling
public sealed class HexalithMyNewModuleApiServerModule :
IApiServerApplicationModule, IMyToDoModule
{
public static void AddServices(IServiceCollection services,
IConfiguration configuration)
{
// Register serialization mappers
HexalithMyNewModuleEventsSerialization.RegisterPolymorphicMappers();
HexalithMyNewModuleCommandsSerialization.RegisterPolymorphicMappers();
// Add module services
services.AddMyToDo();
services.AddMyToDoProjectionActorFactories();
}
public static void RegisterActors(object actorCollection)
{
var actorRegistrations = (ActorRegistrationCollection)actorCollection;
actorRegistrations.RegisterActor<DomainAggregateActor>(
MyToDoDomainHelper.MyToDoAggregateName.ToAggregateActorName());
// ... more actor registrations
}
}
Web Server Module
Location: src/libraries/Infrastructure/Hexalith.MyNewModule.WebServer/
Provides server-side rendering (SSR) support for Blazor.
Web App Module
Location: src/libraries/Infrastructure/Hexalith.MyNewModule.WebApp/
Provides WebAssembly (WASM) client support for Blazor.
Presentation Layer
The presentation layer contains Blazor UI components and pages.
UI Components
Location: src/libraries/Presentation/Hexalith.MyNewModule.UI.Components/
Reusable Blazor components:
MyToDoIdField.razor- ID input fieldMyToDoSummaryGrid.razor- Data grid for summaries
UI Pages
Location: src/libraries/Presentation/Hexalith.MyNewModule.UI.Pages/
Blazor pages:
Home.razor- Module home pageMyToDoIndex.razor- List/index pageMyToDoDetails.razor- Add/edit page
Index Page Example:
@page "/MyToDo/MyToDo"
@rendermode InteractiveAuto
<HexEntityIndexPage
OnLoadData="LoadSummaries"
OnImport="ImportAsync"
OnExport="ExportAsync"
AddPagePath="/MyToDo/Add/MyToDo"
Title="@Labels.ListTitle">
<MyToDoSummaryGrid Items="_summariesQuery"
EntityDetailsPath="/MyToDo/MyToDo"
OnDisabledChanged="OnDisabledChangedAsync" />
</HexEntityIndexPage>
Edit View Model
Location: src/libraries/Presentation/Hexalith.MyNewModule.UI.Pages/MyToDo/
public sealed class MyToDoEditViewModel : IIdDescription, IEntityViewModel
{
public string Id { get; set; }
public string Name { get; set; }
public string? Comments { get; set; }
public bool Disabled { get; set; }
public bool HasChanges => /* change detection logic */;
internal async Task SaveAsync(ClaimsPrincipal user,
ICommandService commandService, bool create,
CancellationToken cancellationToken)
{
// Command submission logic
}
}
Testing
Test Project
Location: test/Hexalith.MyNewModule.Tests/
The project uses:
- xUnit - Testing framework
- Shouldly - Assertion library
- Moq - Mocking framework
Project Structure
test/
└── Hexalith.MyNewModule.Tests/
├── Domains/
│ ├── Aggregates/ # Aggregate tests
│ ├── Commands/ # Command tests
│ └── Events/ # Event tests
└── Hexalith.MyNewModule.Tests.csproj
Writing Tests
public class MyToDoAggregateTests
{
[Fact]
public void Apply_MyToDoAdded_ShouldInitializeAggregate()
{
// Arrange
var aggregate = new MyToDo();
var added = new MyToDoAdded("test-id", "Test Name", "Comments");
// Act
var result = aggregate.Apply(added);
// Assert
result.Succeeded.ShouldBeTrue();
var newAggregate = result.Aggregate as MyToDo;
newAggregate.ShouldNotBeNull();
newAggregate.Id.ShouldBe("test-id");
newAggregate.Name.ShouldBe("Test Name");
}
}
Running Tests
# Run all tests
dotnet test
# Run with coverage
dotnet test --collect:"XPlat Code Coverage"
# Run specific test project
dotnet test test/Hexalith.MyNewModule.Tests/
Configuration
Central Package Management
Package versions are managed centrally in Directory.Packages.props:
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Hexalith.Application" Version="1.71.1" />
<PackageVersion Include="Hexalith.Infrastructure.DaprRuntime" Version="1.71.1" />
</ItemGroup>
</Project>
Build Properties
Build configuration is defined in Directory.Build.props:
<Project>
<PropertyGroup>
<Product>Hexalith.MyNewModule</Product>
<RepositoryUrl>https://github.com/Hexalith/Hexalith.MyNewModule.git</RepositoryUrl>
<PackageProjectUrl>https://github.com/Hexalith/Hexalith.MyNewModule</PackageProjectUrl>
<PackageTags>hexalith;</PackageTags>
<Description>Hexalith MyNewModule</Description>
</PropertyGroup>
</Project>
Application Settings
API Server (HexalithApp/src/HexalithApp.ApiServer/appsettings.json):
- CosmosDB connection settings
- Dapr configuration
- Logging settings
Web Server (HexalithApp/src/HexalithApp.WebServer/appsettings.json):
- Identity provider settings
- Email server configuration
- Session settings
Running with .NET Aspire
Aspire Host
Location: AspireHost/
The Aspire host orchestrates all application components:
HexalithDistributedApplication app = new(args);
if (app.IsProjectEnabled<Projects.HexalithApp_WebServer>())
{
app.AddProject<Projects.HexalithApp_WebServer>("MyNewModuleweb")
.WithEnvironmentFromConfiguration("APP_API_TOKEN")
.WithEnvironmentFromConfiguration("Hexalith__IdentityStores__Microsoft__Id")
// ... more configuration
}
if (app.IsProjectEnabled<Projects.HexalithApp_ApiServer>())
{
app.AddProject<Projects.HexalithApp_ApiServer>("MyNewModuleapi")
// ... configuration
}
await app.Builder.Build().RunAsync();
Running the Application
- Start the Aspire host:
cd AspireHost
dotnet run
- Access the dashboard:
Open the Aspire dashboard URL shown in the console (typically https://localhost:17225).
- Access the application:
- Web Server:
https://localhost:5001 - API Server:
https://localhost:5002
Environment-Specific Configuration
Configuration files are organized by environment in AspireHost/Components/:
Components/
├── Common/
│ ├── Development/
│ ├── Integration/
│ ├── Production/
│ └── Staging/
├── MyNewModuleApi/
│ └── Development/
└── MyNewModuleWeb/
└── Development/
Development Workflow
1. Create a New Feature
- Define domain events in
Domain/Hexalith.MyNewModule.Events/ - Update aggregate in
Domain/Hexalith.MyNewModule.Aggregates/ - Create commands in
Application/Hexalith.MyNewModule.Commands/ - Add request handlers in
Application/Hexalith.MyNewModule.Projections/ - Create/update UI in
Presentation/Hexalith.MyNewModule.UI.Pages/ - Write tests in
test/Hexalith.MyNewModule.Tests/
2. Adding a New Entity
- Create the aggregate record
- Define domain events (Added, Updated, Deleted, etc.)
- Create commands for each operation
- Add request definitions and view models
- Create projection handlers
- Register in the module's
AddServicesmethod - Create UI components and pages
- Add localization resources
3. Code Style
The project uses:
- StyleCop for code analysis
- Global configuration in
Hexalith.globalconfig - XML documentation for public APIs
4. Branching Strategy
main- Production-ready codedevelop- Integration branchfeature/*- Feature branchesbugfix/*- Bug fix branches
Contributing
Prerequisites
- Fork the repository
- Clone your fork
- Set up the development environment
Submitting Changes
- Create a feature branch
- Make your changes
- Write/update tests
- Ensure all tests pass
- Submit a pull request
Code Review Checklist
- Code follows project conventions
- Tests are included and passing
- Documentation is updated
- No breaking changes (or documented if necessary)
- XML documentation for public APIs
Related Repositories
- Hexalith.Builds - Shared build configurations
- HexalithApp - Base application framework
- Hexalith - Core Hexalith libraries
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
- Discord: Join our community
- Issues: GitHub Issues
- Documentation: Wiki
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Hexalith.MyNewModule.Events (>= 1.2.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Hexalith.MyNewModule.Aggregates:
| Package | Downloads |
|---|---|
|
Hexalith.MyNewModule.Requests
Hexalith is a set of libraries to build an application with micro-service architecture. |
GitHub repositories
This package is not used by any popular GitHub repositories.