Skip to main content
Wolfix.Server is built as a modular monolith using .NET 9 and Clean Architecture principles. The application is divided into independent modules that communicate through integration events while sharing a single deployment unit.

Architecture Principles

The architecture follows these key principles:
  • Modular Monolith: Logical separation of concerns within a single deployable application
  • Clean Architecture: Clear separation between Domain, Application, Infrastructure, and Presentation layers
  • Domain-Driven Design: Rich domain models with business logic encapsulated in aggregates
  • Result Pattern: Type-safe error handling without exceptions for business logic failures
  • Event-Driven Communication: Modules communicate via integration events through an in-memory event bus

Module Structure

Wolfix.Server consists of the following modules:

Admin

Admin user management and permissions

Catalog

Product catalog, categories, and reviews

Customer

Customer profiles and account management

Identity

Authentication, authorization, and JWT tokens

Media

File storage and Azure Blob integration

Order

Order processing and Stripe payments

Seller

Seller profiles and applications

Support

Customer support tickets (MongoDB)

Layer Organization

Each module follows a consistent four-layer structure:
{Module}.Domain/          # Entities, Value Objects, Domain Logic
{Module}.Application/     # Use Cases, DTOs, Services
{Module}.Infrastructure/  # Database, External Services
{Module}.Endpoints/       # HTTP Endpoints (Minimal APIs)
{Module}.IntegrationEvents/ # Cross-module event contracts
{Module}.Tests/           # Unit and integration tests

Example: Catalog Module

Catalog.Domain/
  ├── ProductAggregate/
  │   ├── Product.cs              # Aggregate root
  │   ├── Entities/Review.cs      # Child entity
  │   └── ValueObjects/           # Value objects
  ├── CategoryAggregate/
  ├── Interfaces/                 # Repository interfaces
  └── Services/                   # Domain services

Catalog.Application/
  ├── Services/
  │   ├── ProductService.cs       # Application service
  │   └── CategoryService.cs
  ├── Dto/                        # Data transfer objects
  ├── Mapping/                    # DTO mappings
  └── EventHandlers/              # Integration event handlers

Catalog.Infrastructure/
  ├── Repositories/               # EF Core repositories
  ├── Configurations/             # EF entity configurations
  ├── Migrations/                 # Database migrations
  └── Services/
      └── ToxicityService.cs      # External API integration

Catalog.Endpoints/
  └── Endpoints/                  # Minimal API endpoints

Catalog.IntegrationEvents/
  └── ProductCreated.cs           # Event contracts

Shared Components

Common functionality is shared across modules:

Shared.Domain

Base classes and common domain logic:
Shared.Domain/Entities/BaseEntity.cs
namespace Shared.Domain.Entities;

public abstract class BaseEntity
{
    public Guid Id { get; private set; }
}
Shared.Domain/Models/Result.cs
public sealed class Result<TValue>
{
    public TValue? Value { get; }
    public string? ErrorMessage { get; }
    public bool IsSuccess => ErrorMessage == null;
    public bool IsFailure => !IsSuccess;
    public HttpStatusCode StatusCode { get; }

    public static Result<TValue> Success(TValue value, HttpStatusCode statusCode = HttpStatusCode.OK)
        => new(value, statusCode);

    public static Result<TValue> Failure(string errorMessage, HttpStatusCode statusCode = HttpStatusCode.BadRequest)
        => new(errorMessage, statusCode);
}

Shared.Application

Caching, common interfaces, and utilities:
- Caching/            # Memory cache abstractions
- Interfaces/         # Common service interfaces
- Extensions/         # Helper extensions

Shared.Infrastructure

Common infrastructure concerns:
- Repositories/       # Base repository implementations
- ValueGenerators/    # EF Core value generators

Shared.IntegrationEvents

Event bus infrastructure:
- EventBus.cs         # In-memory event bus
- Interfaces/         # Event handler interfaces

Module Communication

Modules communicate exclusively through integration events to maintain loose coupling.

Event Publishing Example

From Identity.Application/Services/AuthService.cs:
Identity.Application/Services/AuthService.cs
public async Task<Result<string>> RegisterAsync(RegisterAsCustomerDto dto, CancellationToken ct)
{
    Result<Guid> registerResult = await authStore.RegisterAccountAsync(
        dto.Email,
        dto.Password,
        Roles.Customer,
        ct
    );

    if (registerResult.IsFailure)
    {
        return Result<string>.Failure(registerResult);
    }
    
    Guid registeredCustomerId = registerResult.Value;

    // Publish integration event to Customer module
    var @event = new CustomerAccountCreated
    {
        AccountId = registeredCustomerId
    };
    
    Result<Guid> createCustomerResult = await eventBus
        .PublishWithSingleResultAsync<CustomerAccountCreated, Guid>(@event, ct);

    // ...
}

Event Handler Example

From Customer.Application/EventHandlers:
public class CustomerAccountCreatedHandler : IIntegrationEventHandler<CustomerAccountCreated, Guid>
{
    public async Task<Result<Guid>> HandleAsync(CustomerAccountCreated @event, CancellationToken ct)
    {
        // Create customer profile
        // Return customer ID
    }
}

Database Strategy

PostgreSQL (EF Core)

Most modules use PostgreSQL with Entity Framework Core:
  • Admin, Catalog, Customer, Identity, Media, Order, Seller
  • Each module has its own DbContext
  • Separate migration history per module
  • Shared database with module-prefixed tables

MongoDB

The Support module uses MongoDB for flexible document storage:
  • Ticket system with varying schemas
  • Automatic index creation on startup
  • Native JSON document storage

Entry Point: Wolfix.API

The Wolfix.API project is the application entry point:
Wolfix.API/Program.cs
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

builder.AddServiceDefaults();

builder.Services.AddAuthorization();
builder.Services.AddAuthentication();

builder
    .AddLoggingToMongoDb()
    .AddAppCache()
    .AddEventBus()
    .AddResponseCompression()
    .AddCors()
    .AddSwaggerJwtBearer();

// Register all module dependencies
await builder.AddAllModules();

// Register exception handling
builder.Services.AddProblemDetails();
builder.Services.AddExceptionHandler<ExceptionHandler>();

WebApplication app = builder.Build();

app.MapDefaultEndpoints();
app.UseStaticFiles();

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseSerilogRequestLogging();
app.UseRouting();
app.UseAuthorization();
app.MapControllers();
app.UseHttpsRedirection();
app.UseExceptionHandler();
app.UseResponseCompression();

// Map all module endpoints
app.MapAllEndpoints();

app.UseCors("AllowNextClient");

app.Run();

Orchestration with .NET Aspire

The Wolfix.AppHost project orchestrates the application and its dependencies:
Wolfix.AppHost/AppHost.cs
var builder = DistributedApplication.CreateBuilder(args);

// Add containers
var toxicApi = builder.AddContainer("toxic-api", "iluhahr/toxic-ai-api:latest")
    .WithHttpEndpoint(targetPort: 8000);

var mongoDb = builder.AddContainer("mongodb-local", "mongo", "latest")
    .WithEndpoint(targetPort: 27017, port: 27017, name: "mongodb", scheme: "tcp");

// Add API with dependencies
builder.AddProject<Projects.Wolfix_API>("api")
    .WithEnvironment("TOXIC_API_BASE_URL", toxicApi.GetEndpoint("http"))
    .WithCustomEnvironmentVariables(envKeyValues)
    .WaitFor(toxicApi)
    .WaitFor(mongoDb);

builder.Build().Run();
.NET Aspire provides a unified dashboard to monitor all services, view logs, and inspect traces.

Next Steps

Modular Monolith

Deep dive into the modular monolith pattern

Clean Architecture

Understand the layered architecture

Domain-Driven Design

Learn about DDD in Wolfix.Server

Result Pattern

Error handling without exceptions

Build docs developers (and LLMs) love