ASP.NET Vertical Slice Architecture Sablon

Tulajdonos
maidics
Létrehozva
2026. máj. 16.
Utolsó push
2026. okt. 13.

Könnyed vertical slice sablon ASP.NET Core-hoz. Kizárólag web API, Aspire-rel összehangolva. Unit, funkcionális és web tesztelés TUnit keretrendszerrel. Elérhető GitHub-on és NuGet-en.

Ez a dokumentum azt bemutatja, hogy milyen kihívásokkal találkoztam a téma feldolgozása során, és hogyan oldottam meg őket.


A MediatR kiváltása

Ezzel a sablonnal egy könnyűsúlyú kiindulópontot szerettem volna adni az ASP.NET Core-os fejlesztéshez.

A vertical slicing egyszerűbb és gyorsabb munkát tesz lehetővé, mint a Clean Architecture, ugyanakkor a sablon kialakításához sok ötletet merítettem Jason Taylor Clean Architecture sablonjából.

Emellett a lehető legkevesebb NuGet-csomagra akartam támaszkodni, így a MediatR-ről is le kellett mondanom.

Az IRequest és IRequestHandler absztrakciók kiváltása meglepően egyszerűnek bizonyult:

  • az interfészeket magam írtam meg,
  • induláskor reflection segítségével regisztrálom őket a DI-ba,
  • közvetlenül injektálhatók a Minimal API endpointokba.

Ezzel viszont elveszítjük azt, ami szerintem a MediatR legértékesebb része: az IPipelineBehavior-t. Sok sablon erre építi például a következőket:

  • logolás (pl. IRequest-enként, illetve teljesítményproblémák vagy kezeletlen Exception esetén),
  • validáció,
  • sőt, akár a jogosultságkezelés is...

Az ASP.NET Core-ban viszont eleve van egy bővíthető pipeline: a HTTP-kérések feldolgozási pipeline-ja. Ezt middleware-rel vagy IEndpointFilter-ekkel lehet kiegészíteni. A naplózást, a validációt és a teljesítményfigyelést ez utóbbival oldottam meg:

  • LoggingFilter: egyszerű filter, amely naplózza a beérkező HTTP-kéréseket; fejlesztéshez implementálva,
  • PerformanceFilter: naplózza azokat a kéréseket, amelyek 500 ms-nál tovább futnak,
  • ValidationFilter:
    • a DI-ból lekéri az összes olyan IValidator szolgáltatást (FluentValidation), amely az endpoint metódus paramétereként kapott IRequest-hez van regisztrálva,
    • ebből következik, hogy az automatikus validációhoz magának az IRequest objektumnak kell az endpoint paraméterének lennie,
    • az IPipelineBehavior-alapú megoldáshoz képest ez hamarabb jelez hibát: a validáció még az endpoint metódus meghívása előtt lefut.

A korábbi verziókban saját domain event implementáció is volt, de több okból kivettem:

  • egymásba ágyazott mentésekhez vezetett az adatbázis-kontextusban,
  • erre a célra léteznek NuGet-csomagok, amelyek jobb megoldásokat kínálnak.

A tesztek felépítése

A tesztek szervezésénél volt néhány kifejezetten junior pillanatom, így a struktúra verzióról verzióra sokat változott:

  • v2.1.0: tesztek a sablonnal együtt szállított kódhoz, például az AuditableEntityInterceptor-hoz vagy a ProblemDetailsExceptionHandler-hez,
  • v2.3.0: a sablon tesztjeit külön projektbe tettem, mert más tesztinfrastruktúrát igényeltek,
  • v2.6.0: külön unit teszt projekt azoknak a teszteknek, amelyekhez nem kell különösebb előkészítés (app host, adatbázis stb.),
  • v3.0.0:
    • NUnit helyett a TUnit tesztkeretrendszerre váltottam,
    • a TUnit-nak köszönhetően minden tesztet egy projektbe vonhattam össze (a sablon tesztjei továbbra is külön maradtak),
    • bekerültek az E2E tesztek, amelyek HttpClient-en keresztül küldenek HTTP-kéréseket,
    • sőt, még a sablon tesztprojektjét is átszerveztem...

A v3.0.0-val az volt a célom, hogy a tesztprojekt is vertical slice struktúrát kövessen, és mindent egyetlen, a fő projekt szerkezetét követő .csproj alá tegyek. Ez nehézkesnek és kuszának bizonyult, mert a különböző tesztek eltérő infrastruktúrát és függőségeket igényelnek.

A v4.0.0-val úgy érzem, megtaláltam a tesztstruktúra végleges formáját:

  • UnitTests:

    • unit és funkcionális tesztek,
    • mockolt szolgáltatások, szerver nélkül, memóriában futó alkalmazás, Aspire által menedzselt adatbázissal (a TestAppHost-ból),
    • egyszerű osztályok, extension metódusok és IRequestHandler implementációk tesztelésére.
  • IntegrationTests:

    • a solution infrastruktúráját célozza, vagyis a külső függőségekkel rendelkező kódot,
    • a sablon ehhez egy üres projektet biztosít,
    • nem hivatkozik a TUnit-ra, hogy a dotnet test ne lépjen ki 8-as kóddal (egyetlen teszt sem futott le).
  • WebTests:

    • E2E tesztek (endpointokon keresztül),
    • memóriában futó alkalmazás Kestrellel, az adatbázist az Aspire biztosítja.

Ez a felállás tisztán elkülönítve lefed mindent, amit a sablon kínál.