Vrstvy a projekty
Devět projektů řešení, co v nich je a proč závislosti vedou jen dovnitř (čistá architektura).
Na této stránce
V jedné větě: Kód je rozdělený do vrstev jako cibule – uprostřed doména (business pravidla), kolem aplikace (use-casy), venku infrastruktura (databáze, RabbitMQ) a hostitelé (Web, Inventory.Api). Vnitřní vrstva nikdy neví o vnější.
Závislosti mezi projekty
flowchart BT
contracts["MiniShop.Contracts<br/><i>zprávy mezi službami</i>"]
domain["MiniShop.Domain<br/><i>entity, doménové události</i>"]
app["MiniShop.Application<br/><i>use-casy, handlery, rozhraní</i>"]
infra["MiniShop.Infrastructure<br/><i>EF Core, outbox, inbox, RabbitMQ</i>"]
defaults["MiniShop.ServiceDefaults<br/><i>health, resilience, OpenTelemetry</i>"]
web["MiniShop.Web<br/><i>služba Ordering + UI</i>"]
invapi["MiniShop.Inventory.Api<br/><i>služba skladu</i>"]
host["MiniShop.AppHost<br/><i>.NET Aspire – spouští vše</i>"]
tests["MiniShop.Tests"]
app --> domain
app --> contracts
infra --> app
web --> infra
web --> defaults
invapi --> infra
invapi --> defaults
host -.->|spouští| web
host -.->|spouští| invapi
tests --> web
tests --> invapi
click domain "/Docs/eda/domenove-udalosti"
click contracts "/Docs/eda/integracni-zpravy"
click infra "/Docs/architektura/sdilene-jadro"
click web "/Docs/architektura/kontejnery"
click invapi "/Docs/architektura/kontejnery"
Šipka znamená „odkazuje na / zná“. Všimněte si, že Domain a Contracts nezávisí na ničem a že Application nezná Infrastructure – zná jen rozhraní, která Infrastructure implementuje.
Projekty jeden po druhém
| Projekt | Co v něm je | Proč samostatně |
|---|---|---|
| MiniShop.Contracts | Integrační zprávy (OrderPlacedV1, ReserveStockV1, …) |
„Veřejné API“ mezi službami. Nemá žádné závislosti, takže ho může sdílet kdokoli. Viz Integrační zprávy. |
| MiniShop.Domain | Entity (Order, Product, OrderSaga…), doménové události, business pravidla |
Srdce aplikace. Nezná databázi ani web – dá se testovat bez čehokoli dalšího. |
| MiniShop.Application | Use-casy (OrderService), handlery událostí, orchestrátor ságy, rozhraní repozitářů |
Říká co se má stát, ne jak se to uloží. Nemá žádné NuGet balíčky (ani DI) – předchází se tím konfliktům verzí. |
| MiniShop.Infrastructure | EF Core DbContexty, repozitáře, outbox, inbox, RabbitMQ, telemetrie |
Technické detaily, které jdou vyměnit (SQLite → PostgreSQL, RabbitMQ → jiný broker) bez zásahu do domény. |
| MiniShop.ServiceDefaults | Společné nastavení služeb: health checky, service discovery, resilience HTTP, OpenTelemetry | Obě služby se mají chovat stejně – jedno místo místo kopírování. |
| MiniShop.Web | Razor Pages, REST API objednávek, background workery, Program.cs |
Hostitel modulů Ordering a Journeys; skládá celou aplikaci (composition root). |
| MiniShop.Inventory.Api | REST API produktů, Program.cs |
Hostitel modulu Inventory. |
| MiniShop.AppHost | Popis celé aplikace pro .NET Aspire | Lokální spuštění jedním příkazem (RabbitMQ + obě služby + dashboard). |
| MiniShop.Tests | Testy domény, služeb, ságy, outboxu, inboxu, kontraktů, architektury a této dokumentace | Testy běží bez RabbitMQ – celá sága v jednom procesu nad SQLite v paměti. |
Composition root – kde se to celé skládá
Application definuje rozhraní (IOrderRepository, IOrderingUnitOfWork…), Infrastructure je implementuje
a propojí je až hostitel při startu (dependency injection):
- Program.cs (Web) a Program.cs (Inventory.Api)
- registrace aplikačních služeb a handlerů: ApplicationServiceCollectionExtensions.cs (ve Webu, protože Application nemá balíček DI)
- registrace infrastruktury: DependencyInjection.cs, OrderingInfrastructure.cs
Přirovnání: Doména je kuchař s receptem. Neřeší, jestli suroviny přivezl dodavatel A nebo B – to zařídí provozní (composition root). Kuchař jen řekne „potřebuju mouku“ (rozhraní).
Proč takhle
- Testovatelnost – doménu a use-casy jde testovat bez databáze a sítě.
- Výměnnost – technologie (DB, broker) jsou za rozhraním. Přechod z volání v paměti na RabbitMQ (krok 8) nezměnil jediný handler.
- Čitelnost – business pravidla jsou na jednom místě (
Order.Confirmhlídá, že potvrdit jde jen čekající objednávku).
Cena: víc projektů a rozhraní, než by malá aplikace nutně potřebovala.