Mursil Documentation

Architecture

Mursil follows Vertical Slice Architecture inside Clean Architecture. Dependencies point inward. Presentation calls Application. Application coordinates Domain. Infrastructure implements what Application defines. Domain references nothing in the outer layers.

Use cases go through MediatR. HTTP endpoints do not contain business rules. Persistence goes through repositories and specifications. Expected business failures use the Result pattern rather than exceptions thrown for validation.

Source layout

src/
├── Host/
│   ├── AppHost/            .NET Aspire: PostgreSQL, Redis, Mailpit, Migrator, Server, Admin
│   └── ServiceDefaults/    health, OpenTelemetry, shared service defaults
└── Mursil/
    ├── Domain/             aggregates, strongly typed ids, smart enums
    ├── Application/        handlers, commands, validators
    ├── Contracts/          DTOs and request types
    ├── Client/             transactional email client seed
    ├── Integration/        host package: send, draft template push, redacted status
    ├── Infrastructure/     EF Core, dispatch, providers
    └── Presentation/
        ├── Mursil.Server/      Minimal API host
        ├── Mursil.Admin/       Blazor admin portal
        └── Mursil.Migrator/    migrations and first-party seeding

Tests live under src/Tests/Mursil/. The solution file is Mursil.slnx.

Dependency flow

Mursil.Server  →  Mursil.Application  →  Mursil.Domain
                         ↓
              CleanArch Pro Communication module
              (low-level dispatch, consumed as NuGet)

The admin portal talks to Server over HTTP through Refit. It does not open the database.

Two delivery layers

LayerOwnsDoes not own
Mursil platformAcceptance, render, idempotency, history, retries, dead-letter, audit, integration eventsThe provider’s SMTP or HTTP call
CleanArch Pro CommunicationEncrypted queue persistence, provider adapter, background executionTemplate lifecycle and portal policy

Product send persists the message first. MursilDeliveryDispatchHostedService then sends it and records Sent, retry, or dead-letter. Handlers do not call a provider SDK directly.

Data stores

PostgreSQL is the system of record, accessed through EF Core. Redis is the cache. Local development also runs Mailpit, which captures SMTP and is not an inbox product. The dev issuer, when enabled, mints local JWTs and has no user database.

Runtime processes

ProcessRole
MigratorApplies migrations and first-party seed, then exits
ServerHTTP API, preference center, tracking pixels, delivery worker
AdminOperator portal

The migrator must finish successfully before Server and Admin serve traffic. Published images are three tags on one private Docker Hub repository: server, admin, and migrator. Postgres, Redis, Mailpit, and the dev issuer are not in that repository.