A modern, event-sourced image sharing platform built with .NET 10 and following Domain-Driven Design (DDD) and Clean Architecture principles.
Nexus is structured as a modular monolith following Clean Architecture with clear separation of concerns:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Presentation β
β (Nexus.Api, AppHost) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Application β
β (Commands, Queries, Handlers) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Domain β
β (Entities, Events, Value Objects, Rules) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Infrastructure β
β (Repositories, External Services) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- Event Sourcing: Domain events are the source of truth using Marten
- CQRS: Separate command and query responsibilities using Wolverine
- Domain-Driven Design: Rich domain model with aggregates, value objects, and domain events
- Clean Architecture: Dependencies flow inward, domain layer has zero external dependencies
- Vertical Slice Architecture: Features organized by business capability
Pure domain layer with zero external dependencies. Contains:
- Entities: Aggregate roots (
ImagePost,TagMigration) - Value Objects: Immutable domain concepts (
Tag,Comment) - Domain Events: Event sourcing events for all state changes
- Domain Errors: Type-safe error handling using Result pattern
- Business Rules: Encapsulated validation and invariants
Application business logic and orchestration:
- Features: Organized by vertical slices (ImagePosts, Tags, Comments)
- Commands/Queries: CQRS command and query handlers using Wolverine
- Projections: Read model projections from event streams
- Validators: FluentValidation rules for commands/queries
- DTOs: Data transfer objects for API responses
External concerns and third-party integrations:
- Repositories: Data access implementations
- External Services: AWS S3 integration via LocalStack
- Image Processing: SkiaSharp-based image conversion and thumbnails
HTTP API layer using minimal APIs:
- Endpoints: HTTP endpoints for images and tags
- Middleware: Authentication, error handling, request/response logging
- API Documentation: OpenAPI/Scalar integration
.NET Aspire orchestration host:
- Coordinates all services and dependencies
- Manages PostgreSQL, RabbitMQ, LocalStack containers
- Provides observability and telemetry
Background worker service:
- Processes image upload events from RabbitMQ queue
- Converts images to WebP format
- Generates multiple thumbnail sizes
- Updates image post status via events
Shared configuration for all services:
- OpenTelemetry configuration
- Health checks
- Common middleware
Database schema management:
- SQL migration scripts
- Version-controlled schema changes
- Integrated with Marten's
db-patchtooling
Domain layer unit tests:
- Entity behavior and business rules
- Value object validation
- Domain event generation
Application layer unit tests:
- Command/query handler logic
- Projection behavior
- Validation rules
Architecture validation tests using NetArchTest:
- Layer dependency rules
- Naming conventions
- DDD pattern enforcement
- See Architecture Tests README for details
Full-stack integration tests:
- HTTP endpoint testing
- End-to-end workflows
Image processor service integration tests
Database migration validation tests
Shared test utilities:
- AutoFixture extensions
- Test data builders
Shared integration test infrastructure:
- Test fixtures
- Database seeding helpers
Install .NET Aspire:
dotnet workload update
dotnet workload install aspire-
Clone the repository
git clone <repository-url> cd Nexus
-
Start the application with Aspire
dotnet run --project Nexus.AppHost
This will start:
- Nexus.Api (HTTP API)
- Nexus.ImageProcessor (Background worker)
- PostgreSQL (Database)
- RabbitMQ (Message broker)
- LocalStack (AWS S3 emulator)
- Aspire Dashboard (Observability)
-
Access the services
When running via Aspire AppHost, services are assigned dynamic ports. Access them through the Aspire Dashboard:
- Aspire Dashboard: http://localhost:15017 (check console output for exact URL)
When running the API directly (without Aspire):
- API: http://localhost:5157 or https://localhost:7201
- API Documentation (Scalar): https://localhost:7201/scalar/v1
Note: .NET Aspire assigns ports dynamically for each service. Use the Aspire Dashboard to find the actual endpoint URLs for each service.
# Run all tests
dotnet test
# Run specific test project
dotnet test Nexus.Domain.UnitTests
dotnet test Nexus.Architecture.Tests
# Run with coverage
dotnet test --collect:"XPlat Code Coverage"- .NET 10: Latest .NET platform
- C# 13: Modern C# language features
- Marten: Event store and document database on PostgreSQL
- Wolverine: Next-generation message handling and CQRS
- PostgreSQL: Event store and read model database
- RabbitMQ: Asynchronous message broker
- Wolverine.RabbitMQ: RabbitMQ integration
- SkiaSharp: Cross-platform image manipulation
- AWS S3: Object storage (via LocalStack in dev)
- LocalStack: Local AWS cloud stack emulator
- Docker: Containerization
- .NET Aspire: Cloud-ready orchestration and observability
- OpenTelemetry: Distributed tracing and metrics
- Serilog: Structured logging
- xUnit: Test framework with Microsoft.Testing.Platform runner
- AutoFixture: Test data generation
- FluentValidation: Input validation
- NetArchTest: Architecture rule enforcement
- Minimal APIs: Lightweight HTTP endpoints
- Scalar: Modern OpenAPI documentation UI
- Define Domain Events in
Nexus.Domain/Events/ - Create/Update Entities in
Nexus.Domain/Entities/ - Write Unit Tests in
Nexus.Domain.UnitTests/ - Create Command/Query in
Nexus.Application/Features/ - Add Handler for the command/query
- Create Projection for read models (if needed)
- Add Endpoint in
Nexus.Api/Endpoints/ - Write Integration Tests in
Nexus.Api.IntegrationTests/
The solution includes tooling to generate and apply database migrations:
# Generate a new migration
dotnet run --project Nexus.AppHost -- db-patch <migration-name>
# Example
dotnet run --project Nexus.AppHost -- db-patch add_user_profileThis creates a timestamped SQL file in Nexus.Migrations/Scripts/.
- Entities: Must inherit from
BaseEntityand be sealed - Value Objects: Must inherit from
ValueObjectand be sealed - Domain Events: Must implement
INexusEventand end with "DomainEvent" - Commands: End with "Command"
- Queries: End with "Query"
- Validators: End with "Validator"
- DTOs: End with "Dto"
These conventions are enforced by architecture tests. See Architecture Tests README.
The main aggregate representing an image post:
- User-uploaded images with title and tags
- Comments from users
- Upload status lifecycle (Pending β Processing β Completed/Failed)
- Event-sourced: rebuilt from domain events
Manages tag renaming and consolidation:
- Allows migrating posts from one tag to another
- Maintains tag consistency across the system
All state changes are captured as domain events:
ImagePostCreatedDomainEventTagAddedDomainEvent/TagRemovedDomainEventCommentCreatedDomainEvent/CommentUpdatedDomainEvent/CommentDeletedDomainEventStatusChangedDomainEventTagMigratedDomainEvent
- Tag: Immutable tag with type and value
- Comment: Immutable comment with content and metadata
- TagData: Data structure for tag type and value
Configuration is managed through appsettings.json and environment-specific overrides:
{
"ImageOptions": {
"OriginalImageBucketName": "nexus-original-images",
"ProcessedImageBucketName": "nexus-processed-images",
"ThumbnailBucketName": "nexus-thumbnails",
"ProcessedImagePublicDomain": "https://nexus-processed-images.s3.localhost.localstack.cloud:4566",
"ThumbnailPublicDomain": "https://nexus-thumbnails.s3.localhost.localstack.cloud:4566"
},
"LocalStack": {
"UseLocalStack": true
}
}In development, .NET Aspire manages connection strings automatically. For production:
{
"ConnectionStrings": {
"postgres": "Host=localhost;Database=nexus;Username=postgres;Password=***",
"rabbitmq": "amqp://guest:guest@localhost:5672"
}
}- Follow the existing architecture and coding patterns
- Write tests for new features
- Run architecture tests to ensure compliance:
dotnet test Nexus.Architecture.Tests - Ensure all tests pass before submitting changes
- Follow event sourcing principles - state changes through events only
[Your License Here]
- .NET Aspire Documentation
- Marten Event Sourcing
- Wolverine Documentation
- Domain-Driven Design Reference
- Architecture Tests README
Built with β€οΈ using .NET 10 and modern software architecture principles.