Files
the-biergarten-app/docs/web/diagrams/vertical-slice.md

4.6 KiB

The Biergarten App — Vertical Slice Architecture

Two diagrams, split by concern: which host wires up which slice at startup, and which projects each slice actually compiles against. Mixing DI wiring and compile-time references in one graph is what made the old diagram unreadable.

Host wiring

Each host (API.Core, Database.Seed) decides for itself which Features.* assemblies to register — there's no shared bootstrap. Database.Migrations doesn't wire up any slice; it applies schema directly.

--8<-- "web/diagrams/vertical-slice/host-wiring.mmd"

Project reference dependencies

Solid arrows are compile-time project references — every slice-to-slice dependency is one now that the email MediatR contract has moved off Shared.Application: Features.Users on Features.PhotoUpload for avatar uploads, and Features.Emails on Features.Users for the registration and resend-confirmation notifications. Every DB-backed slice depends on the same three foundation projects (Shared.Contracts, Domain.Entities, Database.Connection) — drawn as one box instead of fifteen separate arrows; see the notes for what else each of those foundation projects pulls in.

--8<-- "web/diagrams/vertical-slice/project-references.mmd"

Notes

  • API.Core: Program.cs wiring only

    • MediatR + AddApplicationPart per HTTP-exposed Features.* assembly
    • Swagger/OpenAPI, health checks, JWT auth middleware, global exception filter
    • Only Features.Users and Features.Breweries get AddApplicationPart (controllers)
    • Features.PhotoUpload and Features.Emails scanned for MediatR handlers only
    • Features.Locations and Features.Beers not referenced by API.Core at all
  • Feature slices: each owns its own controller (where present), commands/queries + handlers, validators, and Dapper repository

    • Features.Users covers auth (Commands/Authentication), account management (Commands/Account), and profile/avatar (Commands/Profile)
    • Features.Emails and Features.PhotoUpload have no controller — invoked only via MediatR commands sent from other slices
    • Features.Locations has no controller; one MediatR handler (GetOrCreateCityHandler) registered by Database.Seed only — API.Core never scans it
    • Features.Beers is scaffolded (project + references only, no source files yet)
  • The exceptions to slice isolation:

    • Features.Users takes a direct project reference to Features.PhotoUpload
      • sends its UploadPhotoCommand via MediatR from UploadAvatarHandler
      • the command type lives in the target slice itself, not in Shared.Application
    • Features.Emails takes a direct project reference to Features.Users
      • handles UserRegisteredNotification (published from RegisterUserHandler) and ConfirmationEmailResendRequestedNotification (published from ResendConfirmationEmailHandler)
      • both notification types live in Features.Users (Features.Users.Notifications), not in Shared.Application
  • Shared.Application:

    • ValidationBehavior (MediatR pipeline)
    • Every slice (including Features.Emails) references it for the pipeline behavior alone
  • Domain.Entities:

    • UserAccount, UserCredential, UserVerification, UserProfile, UserAvatar
    • BreweryPost, BreweryPostLocation
    • City, StateProvince, Country
    • BeerPost, BeerStyle, Photo (schema tables exist for BeerPost/BeerStyle; no repository yet since Features.Beers has no code)
  • Database.Connection: the shared connection-factory project

    • each slice's own repository issues inline SQL via Dapper against it
    • no ORM, no stored procedures
    • Replaces the old Infrastructure.Sql project
  • Database.Migrations: separate console app

    • schema.dbml + SQL scripts
    • applies DDL directly
    • distinct from Database.Seed, which loads data through the feature slices' own DI extensions
  • Database.Seed:

    • calls each slice's AddFeatures*() DI extension directly
    • builds its own MediatR registration
    • no API host, no shared pipeline with API.Core
  • Omitted from the diagram:

    • Domain.Exceptions (referenced by nearly every project directly, including all of Shared.Contracts and each feature slice)
    • Configuration (the shared options-binding project referenced by Database.Connection, Features.PhotoUpload, and Features.Emails directly)
    • both are plumbing-level dependencies of nearly everything above, and drawing them would just add a second copy of the same fan-in