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
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Hexalith.GitStorage.Events" Version="1.4.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Hexalith.GitStorage.Events" Version="1.4.0" />
                    
Directory.Packages.props
<PackageReference Include="Hexalith.GitStorage.Events" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Hexalith.GitStorage.Events --version 1.4.0
                    
#r "nuget: Hexalith.GitStorage.Events, 1.4.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Hexalith.GitStorage.Events@1.4.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Hexalith.GitStorage.Events&version=1.4.0
                    
Install as a Cake Addin
#tool nuget:?package=Hexalith.GitStorage.Events&version=1.4.0
                    
Install as a Cake Tool

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

License: MIT Discord

Coverity Scan Build Status Codacy Badge Quality Gate Status Security Rating Maintainability Rating Code Smells Lines of Code Technical Debt Reliability Rating Duplicated Lines (%) Vulnerabilities Bugs

Build status NuGet Latest

Table of Contents

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

  1. Domain-Driven Design (DDD): Domain logic is encapsulated in aggregates with clear boundaries
  2. CQRS: Commands (writes) and Queries (reads) are separated into different models
  3. Event Sourcing: State changes are captured as a sequence of events
  4. Clean Architecture: Dependencies flow inward, with the domain layer at the core
  5. Modular Design: Each module is self-contained and can be deployed independently

Prerequisites

Before getting started, ensure you have the following installed:

Optional Tools

Getting Started

1. Clone or Use as Template

Option A: Use as GitHub Template

  1. Click "Use this template" on GitHub
  2. 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 GitStorageAccount with 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 IDomainAggregate interface
  • Uses C# records for immutability
  • Primary constructors for clean initialization
  • Apply method 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 created
  • GitStorageAccountDescriptionChanged - When account details are updated
  • GitStorageAccountDisabled - When an account is disabled
  • GitStorageAccountEnabled - When an account is enabled
Organization Events
  • OrganizationAdded - When a new organization is registered
  • OrganizationUpdated - When organization details are updated
  • OrganizationRemoved - When an organization is removed
Repository Events
  • RepositoryCreated - When a new repository is created
  • RepositoryUpdated - When repository settings are changed
  • RepositoryArchived - When a repository is archived
  • RepositoryDeleted - 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 - French
  • GitStorageMenu.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 interface
  • IGitStorageAccountService.cs - Service interface
  • GitStorageAccountPolicies.cs - Authorization policies
  • GitStorageAccountRoles.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 connection
  • ChangeGitStorageAccountDescription - Update account details
  • DisableGitStorageAccount - Disable an account
  • EnableGitStorageAccount - Enable an account
Organization Commands
  • CreateOrganization - Create a new organization
  • UpdateOrganization - Update organization settings
  • DeleteOrganization - Remove an organization
Repository Commands
  • CreateRepository - Create a new repository
  • UpdateRepository - Update repository settings
  • ArchiveRepository - Archive a repository
  • DeleteRepository - 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 account
  • GetGitStorageAccountSummaries - Get list of account summaries
  • GetGitStorageAccountIds - Get list of all account IDs
Organization Requests
  • GetOrganizations - List organizations for an account
  • GetOrganizationDetails - Get organization details
Repository Requests
  • GetRepositories - List repositories for an organization
  • GetRepositoryDetails - Get repository details

View Models

View models for presenting data to the UI.

  • GitStorageAccountDetailsViewModel - Full account details
  • GitStorageAccountSummaryViewModel - Account summary for lists
  • OrganizationViewModel - Organization details
  • RepositoryViewModel - 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 field
  • GitStorageAccountSummaryGrid.razor - Data grid for accounts
  • OrganizationSelector.razor - Organization selection component
  • RepositoryList.razor - Repository listing component
  • ProviderTypeSelector.razor - GitHub/Forgejo provider selector

UI Pages

Location: src/libraries/Presentation/Hexalith.GitStorage.UI.Pages/

Blazor pages:

  • Home.razor - Module home page
  • GitStorageAccountIndex.razor - Account list page
  • GitStorageAccountDetails.razor - Account add/edit page
  • OrganizationIndex.razor - Organization list page
  • RepositoryIndex.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

  1. Implement the provider adapter in Infrastructure layer
  2. Add provider-specific configuration
  3. Register the provider in service collection
  4. Add UI components for provider-specific features

Adding a New Entity

  1. Create the aggregate record
  2. Define domain events
  3. Create commands for each operation
  4. Add request definitions and view models
  5. Create projection handlers
  6. Create UI components and pages
  7. Add localization resources

Contributing

See CONTRIBUTING.md for detailed guidelines.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Support

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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.

Version Downloads Last Updated
1.4.0 244 12/7/2025
1.3.0 233 12/7/2025
1.2.0 236 12/7/2025
1.1.0 603 12/1/2025
1.0.0 597 12/1/2025