Hexalith.GitStorage.Events
1.4.0
dotnet add package Hexalith.GitStorage.Events --version 1.4.0
NuGet\Install-Package Hexalith.GitStorage.Events -Version 1.4.0
<PackageReference Include="Hexalith.GitStorage.Events" Version="1.4.0" />
<PackageVersion Include="Hexalith.GitStorage.Events" Version="1.4.0" />
<PackageReference Include="Hexalith.GitStorage.Events" />
paket add Hexalith.GitStorage.Events --version 1.4.0
#r "nuget: Hexalith.GitStorage.Events, 1.4.0"
#:package Hexalith.GitStorage.Events@1.4.0
#addin nuget:?package=Hexalith.GitStorage.Events&version=1.4.0
#tool nuget:?package=Hexalith.GitStorage.Events&version=1.4.0
Hexalith.GitStorage
A comprehensive module for managing Git storage providers including GitHub and Forgejo. This module enables organizations and repositories to be created, updated, and managed through a unified API 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
Hexalith.GitStorage provides a unified service layer for managing Git storage providers. It abstracts the differences between GitHub and Forgejo APIs, allowing applications to:
- Manage Git Storage Accounts: Configure and manage connections to GitHub and Forgejo instances
- Manage Organizations: Create, update, and configure organizations across providers
- Manage Repositories: Create, update, configure, and manage repositories with full lifecycle support
The module implements a clean architecture with clear separation of concerns:
- Domain Layer: Contains domain aggregates, events, and value objects for Git entities
- Application Layer: Contains commands, command handlers, requests, and projections
- Infrastructure Layer: Contains API servers, web servers, and provider integrations
- 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.
Supported Providers
| Provider | Features | Authentication |
|---|---|---|
| GitHub | Full API support for organizations and repositories | Personal Access Token, OAuth App, GitHub App |
| Forgejo | Full API support for organizations and repositories | Personal Access Token, OAuth2 |
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.GitStorage.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
GitStorageAccountwith your package name - Rename directories and files containing
GitStorageAccount - 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.GitStorage/
├── AspireHost/ # .NET Aspire orchestration
│ ├── Program.cs # Aspire app configuration
│ ├── Components/ # Aspire component configurations
│ └── appsettings.json # Application settings
│
├── src/ # Source code root
│ ├── HexalithGitStorageApiServerApplication.cs
│ ├── HexalithGitStorageWebAppApplication.cs
│ ├── HexalithGitStorageWebServerApplication.cs
│ │
│ └── libraries/ # NuGet package projects
│ │
│ ├── Application/ # Application layer
│ │ ├── Hexalith.GitStorage/ # Main application logic
│ │ ├── Hexalith.GitStorage.Abstractions/ # Interfaces & contracts
│ │ ├── Hexalith.GitStorage.Commands/ # Command definitions
│ │ ├── Hexalith.GitStorage.Projections/ # Read model projections
│ │ └── Hexalith.GitStorage.Requests/ # Query requests
│ │
│ ├── Domain/ # Domain layer
│ │ ├── Hexalith.GitStorage.Aggregates/ # Domain aggregates
│ │ ├── Hexalith.GitStorage.Aggregates.Abstractions/# Domain helpers
│ │ ├── Hexalith.GitStorage.Events/ # Domain events
│ │ └── Hexalith.GitStorage.Localizations/ # Resource files
│ │
│ ├── Infrastructure/ # Infrastructure layer
│ │ ├── Hexalith.GitStorage.ApiServer/ # REST API server
│ │ ├── Hexalith.GitStorage.Servers/ # Server helpers
│ │ ├── Hexalith.GitStorage.WebApp/ # WASM client module
│ │ └── Hexalith.GitStorage.WebServer/ # SSR web server module
│ │
│ └── Presentation/ # Presentation layer
│ ├── Hexalith.GitStorage.UI.Components/ # Blazor components
│ └── Hexalith.GitStorage.UI.Pages/ # Blazor pages
│
├── test/ # Test projects
│ └── Hexalith.GitStorage.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.GitStorage.sln # Solution file
└── initialize.ps1 # Initialization script
Domain Layer
The domain layer contains the core business logic and is framework-agnostic.
Domain Entities
The module manages three main domain entities:
| Entity | Description |
|---|---|
| GitStorageAccount | Represents a connection to a Git provider (GitHub or Forgejo) |
| Organization | Represents an organization within a Git provider |
| Repository | Represents a Git repository within an organization |
Aggregates
Aggregates are the core domain entities that encapsulate business rules and state changes.
Location: src/libraries/Domain/Hexalith.GitStorage.Aggregates/
/// <summary>
/// Represents a Git storage account aggregate for managing provider connections.
/// </summary>
/// <param name="Id">The account identifier.</param>
/// <param name="Name">The account display name.</param>
/// <param name="ProviderType">The provider type (GitHub or Forgejo).</param>
/// <param name="BaseUrl">The base URL for the provider API.</param>
/// <param name="Comments">Optional description.</param>
/// <param name="Disabled">Whether the account is disabled.</param>
[DataContract]
public sealed record GitStorageAccount(
[property: DataMember(Order = 1)] string Id,
[property: DataMember(Order = 2)] string Name,
[property: DataMember(Order = 3)] GitProviderType ProviderType,
[property: DataMember(Order = 4)] string BaseUrl,
[property: DataMember(Order = 5)] string? Comments,
[property: DataMember(Order = 6)] 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.GitStorage.Events/
GitStorageAccount Events
GitStorageAccountAdded- When a new provider connection is createdGitStorageAccountDescriptionChanged- When account details are updatedGitStorageAccountDisabled- When an account is disabledGitStorageAccountEnabled- When an account is enabled
Organization Events
OrganizationAdded- When a new organization is registeredOrganizationUpdated- When organization details are updatedOrganizationRemoved- When an organization is removed
Repository Events
RepositoryCreated- When a new repository is createdRepositoryUpdated- When repository settings are changedRepositoryArchived- When a repository is archivedRepositoryDeleted- When a repository is deleted
/// <summary>
/// Event raised when a new Git storage account is added.
/// </summary>
[PolymorphicSerialization]
public partial record GitStorageAccountAdded(
string Id,
[property: DataMember(Order = 2)] string Name,
[property: DataMember(Order = 3)] GitProviderType ProviderType,
[property: DataMember(Order = 4)] string BaseUrl,
[property: DataMember(Order = 5)] string? Comments)
: GitStorageAccountEvent(Id);
Value Objects
Value objects are immutable domain concepts with no identity.
Location: src/libraries/Domain/Hexalith.GitStorage.Aggregates.Abstractions/ValueObjects/
GitProviderType- Enum for provider types (GitHub, Forgejo)RepositoryVisibility- Enum for repository visibility (Public, Private)OrganizationSettings- Configuration for organizations
Localizations
Resource files for internationalization (i18n) support.
Location: src/libraries/Domain/Hexalith.GitStorage.Localizations/
Files:
GitStorageAccount.resx- English (default)GitStorageAccount.fr.resx- FrenchGitStorageMenu.resx- Menu labels
Application Layer
The application layer coordinates domain operations and implements use cases.
Abstractions
Location: src/libraries/Application/Hexalith.GitStorage.Abstractions/
Key files:
IGitStorageAccountModule.cs- Module interfaceIGitStorageAccountService.cs- Service interfaceGitStorageAccountPolicies.cs- Authorization policiesGitStorageAccountRoles.cs- Security roles
/// <summary>
/// Defines the roles for GitStorageAccount security.
/// </summary>
public static class GitStorageAccountRoles
{
public const string Owner = nameof(GitStorage) + nameof(Owner);
public const string Contributor = nameof(GitStorage) + nameof(Contributor);
public const string Reader = nameof(GitStorage) + nameof(Reader);
}
Commands
Commands represent intent to change the system state.
Location: src/libraries/Application/Hexalith.GitStorage.Commands/
GitStorageAccount Commands
AddGitStorageAccount- Create a new provider connectionChangeGitStorageAccountDescription- Update account detailsDisableGitStorageAccount- Disable an accountEnableGitStorageAccount- Enable an account
Organization Commands
CreateOrganization- Create a new organizationUpdateOrganization- Update organization settingsDeleteOrganization- Remove an organization
Repository Commands
CreateRepository- Create a new repositoryUpdateRepository- Update repository settingsArchiveRepository- Archive a repositoryDeleteRepository- Delete a repository
/// <summary>
/// Command to add a new Git storage account.
/// </summary>
[PolymorphicSerialization]
public partial record AddGitStorageAccount(
string Id,
[property: DataMember(Order = 2)] string Name,
[property: DataMember(Order = 3)] GitProviderType ProviderType,
[property: DataMember(Order = 4)] string BaseUrl,
[property: DataMember(Order = 5)] string? Comments)
: GitStorageAccountCommand(Id);
Requests (Queries)
Requests represent queries for data retrieval.
Location: src/libraries/Application/Hexalith.GitStorage.Requests/
GitStorageAccount Requests
GetGitStorageAccountDetails- Get full details of an accountGetGitStorageAccountSummaries- Get list of account summariesGetGitStorageAccountIds- Get list of all account IDs
Organization Requests
GetOrganizations- List organizations for an accountGetOrganizationDetails- Get organization details
Repository Requests
GetRepositories- List repositories for an organizationGetRepositoryDetails- Get repository details
View Models
View models for presenting data to the UI.
GitStorageAccountDetailsViewModel- Full account detailsGitStorageAccountSummaryViewModel- Account summary for listsOrganizationViewModel- Organization detailsRepositoryViewModel- Repository details
Projections
Projections handle event processing to update read models.
Location: src/libraries/Application/Hexalith.GitStorage.Projections/
public static class GitStorageAccountProjectionHelper
{
public static IServiceCollection AddGitStorageAccountProjectionHandlers(
this IServiceCollection services)
=> services
.AddScoped<IProjectionUpdateHandler<GitStorageAccountAdded>,
GitStorageAccountAddedOnSummaryProjectionHandler>()
.AddScoped<IProjectionUpdateHandler<GitStorageAccountDescriptionChanged>,
GitStorageAccountDescriptionChangedOnSummaryProjectionHandler>()
// ... more handlers
}
Infrastructure Layer
The infrastructure layer contains technical implementations and hosting concerns.
API Server Module
Location: src/libraries/Infrastructure/Hexalith.GitStorage.ApiServer/
Provides:
- REST API controllers for Git storage operations
- Dapr actor registrations for event sourcing
- GitHub and Forgejo API integrations
- Service registrations and configuration
public sealed class HexalithGitStorageApiServerModule :
IApiServerApplicationModule, IGitStorageAccountModule
{
public static void AddServices(IServiceCollection services,
IConfiguration configuration)
{
// Register serialization mappers
HexalithGitStorageEventsSerialization.RegisterPolymorphicMappers();
HexalithGitStorageCommandsSerialization.RegisterPolymorphicMappers();
// Add module services
services.AddGitStorageAccount();
services.AddGitStorageAccountProjectionActorFactories();
}
public static void RegisterActors(object actorCollection)
{
var actorRegistrations = (ActorRegistrationCollection)actorCollection;
actorRegistrations.RegisterActor<DomainAggregateActor>(
GitStorageAccountDomainHelper.GitStorageAccountAggregateName.ToAggregateActorName());
// ... more actor registrations
}
}
Web Server Module
Location: src/libraries/Infrastructure/Hexalith.GitStorage.WebServer/
Provides server-side rendering (SSR) support for Blazor.
Web App Module
Location: src/libraries/Infrastructure/Hexalith.GitStorage.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.GitStorage.UI.Components/
Reusable Blazor components:
GitStorageAccountIdField.razor- Account ID input fieldGitStorageAccountSummaryGrid.razor- Data grid for accountsOrganizationSelector.razor- Organization selection componentRepositoryList.razor- Repository listing componentProviderTypeSelector.razor- GitHub/Forgejo provider selector
UI Pages
Location: src/libraries/Presentation/Hexalith.GitStorage.UI.Pages/
Blazor pages:
Home.razor- Module home pageGitStorageAccountIndex.razor- Account list pageGitStorageAccountDetails.razor- Account add/edit pageOrganizationIndex.razor- Organization list pageRepositoryIndex.razor- Repository list page
@page "/GitStorage/Accounts"
@rendermode InteractiveAuto
<HexEntityIndexPage
OnLoadData="LoadSummaries"
OnImport="ImportAsync"
OnExport="ExportAsync"
AddPagePath="/GitStorage/Add/Account"
Title="@Labels.ListTitle">
<GitStorageAccountSummaryGrid Items="_summariesQuery"
EntityDetailsPath="/GitStorage/Account"
OnDisabledChanged="OnDisabledChangedAsync" />
</HexEntityIndexPage>
Edit View Model
Location: src/libraries/Presentation/Hexalith.GitStorage.UI.Pages/GitStorageAccount/
public sealed class GitStorageAccountEditViewModel : IIdDescription, IEntityViewModel
{
public string Id { get; set; }
public string Name { get; set; }
public GitProviderType ProviderType { get; set; }
public string BaseUrl { 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.GitStorage.Tests/
The project uses:
- xUnit - Testing framework
- Shouldly - Assertion library
- Moq - Mocking framework
Project Structure
test/
└── Hexalith.GitStorage.Tests/
├── Domains/
│ ├── Aggregates/ # Aggregate tests
│ ├── Commands/ # Command tests
│ └── Events/ # Event tests
└── Hexalith.GitStorage.Tests.csproj
Writing Tests
public class GitStorageAccountAggregateTests
{
[Fact]
public void Apply_GitStorageAccountAdded_ShouldInitializeAggregate()
{
// Arrange
var aggregate = new GitStorageAccount();
var added = new GitStorageAccountAdded(
"github-main",
"GitHub Main",
GitProviderType.GitHub,
"https://api.github.com",
"Primary GitHub account");
// Act
var result = aggregate.Apply(added);
// Assert
result.Succeeded.ShouldBeTrue();
var newAggregate = result.Aggregate as GitStorageAccount;
newAggregate.ShouldNotBeNull();
newAggregate.Id.ShouldBe("github-main");
newAggregate.ProviderType.ShouldBe(GitProviderType.GitHub);
}
}
Running Tests
# Run all tests
dotnet test
# Run with coverage
dotnet test --collect:"XPlat Code Coverage"
# Run specific test project
dotnet test test/Hexalith.GitStorage.Tests/
Configuration
Application Settings
{
"GitStorage": {
"GitHub": {
"BaseUrl": "https://api.github.com",
"Token": "your-github-token"
},
"Forgejo": {
"BaseUrl": "https://your-forgejo-instance.com/api/v1",
"Token": "your-forgejo-token"
}
}
}
User Secrets
For local development, use user secrets to store sensitive configuration:
cd AspireHost
dotnet user-secrets set "GitStorage:GitHub:Token" "your-github-token"
dotnet user-secrets set "GitStorage:Forgejo:Url" "https://your-forgejo-instance.com"
dotnet user-secrets set "GitStorage:Forgejo:Token" "your-forgejo-token"
Central Package Management
Package versions are managed centrally in Directory.Packages.props.
Running with .NET Aspire
Start the Application
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
Development Workflow
Adding a New Provider
- Implement the provider adapter in Infrastructure layer
- Add provider-specific configuration
- Register the provider in service collection
- Add UI components for provider-specific features
Adding a New Entity
- Create the aggregate record
- Define domain events
- Create commands for each operation
- Add request definitions and view models
- Create projection handlers
- Create UI components and pages
- Add localization resources
Contributing
See CONTRIBUTING.md for detailed guidelines.
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
- FluentValidation (>= 12.1.1)
- Hexalith.Domains.Abstractions (>= 1.2.1)
- Hexalith.GitStorage.Aggregates.Abstractions (>= 1.4.0)
- Hexalith.GitStorage.Localizations (>= 1.4.0)
- Hexalith.PolymorphicSerializations (>= 1.9.1)
- Microsoft.Extensions.Localization.Abstractions (>= 10.0.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Hexalith.GitStorage.Events:
| Package | Downloads |
|---|---|
|
Hexalith.GitStorage.Aggregates
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.