arhitektuur.md
11,794 bytes
| 1 | # SplitApp Onion Arhitektuur — joonis ja võrdlus TalTech-i loenguga |
|---|---|
| 2 | |
| 3 | ## 1. Sibula-joonis (ringid seestpoolt väljapoole) |
| 4 | |
| 5 | ``` |
| 6 | ┌─────────────────────────────────────────────────┐ |
| 7 | │ WebApp (Presentation) │ |
| 8 | │ MVC Controllers · API Controllers (v1) │ |
| 9 | │ Areas/Admin · Areas/Identity · Views │ |
| 10 | │ ViewModels · Program.cs (DI Composition Root) │ |
| 11 | │ ┌───────────────────────────────────────────┐ │ |
| 12 | │ │ App.BLL (Application Services) │ │ |
| 13 | │ │ TripService · ExpenseService · │ │ |
| 14 | │ │ SettlementService · AdminServices │ │ |
| 15 | │ │ (kasutab IAppUnitOfWork liidese kaudu) │ │ |
| 16 | │ │ ┌─────────────────────────────────────┐ │ │ |
| 17 | │ │ │ App.Domain (Core / Süda) │ │ │ |
| 18 | │ │ │ Entities: Trip, Expense, ... │ │ │ |
| 19 | │ │ │ Enums, AppUser, AppRole │ │ │ |
| 20 | │ │ │ Contracts: IAppUnitOfWork, │ │ │ |
| 21 | │ │ │ ITripRepository, ... │ │ │ |
| 22 | │ │ │ ┌───────────────────────────────┐ │ │ │ |
| 23 | │ │ │ │ Base.Domain · Base.Contracts │ │ │ │ |
| 24 | │ │ │ │ BaseEntity · IBaseRepository │ │ │ │ |
| 25 | │ │ │ │ IUnitOfWork · LangStr │ │ │ │ |
| 26 | │ │ │ └───────────────────────────────┘ │ │ │ |
| 27 | │ │ └─────────────────────────────────────┘ │ │ |
| 28 | │ └───────────────────────────────────────────┘ │ |
| 29 | └─────────────────────────────────────────────────┘ |
| 30 | ▲ ▲ |
| 31 | │ │ |
| 32 | │ ┌────────────────────────────────────┐ │ |
| 33 | │ │ App.DAL.EF (Infrastructure) │ │ |
| 34 | │ │ AppDbContext · AppUnitOfWork │─── implementeerib ───┘ |
| 35 | │ │ Repositories · Migrations │ IAppUnitOfWork, |
| 36 | │ │ Seeding · ServiceCollectionExt │ ITripRepository |
| 37 | │ └────────────────────────────────────┘ |
| 38 | │ ▲ |
| 39 | │ │ plugitav (saab vahetada nt Dapperi vastu) |
| 40 | │ |
| 41 | │ ┌────────────────────────────────────┐ |
| 42 | └────│ App.DTO · App.Resources │ |
| 43 | │ v1 DTOs · Mappers · .resx │ |
| 44 | └────────────────────────────────────┘ |
| 45 | ``` |
| 46 | |
| 47 | **Tähtis**: Infrastructure (DAL.EF) on **väljaspool** sibulat ja **osutab sissepoole** — just nagu TalTech-i loengus: *"Infrastructure -> Domain <- Application <- Web"*. |
| 48 | |
| 49 | --- |
| 50 | |
| 51 | ## 2. Sõltuvuste graaf (nooled = `ProjectReference`) |
| 52 | |
| 53 | ``` |
| 54 | Base.Domain ◄──── Base.Contracts |
| 55 | ▲ ▲ |
| 56 | │ │ |
| 57 | └──── App.Domain ─┤ |
| 58 | ▲ │ |
| 59 | ┌────────┼───────┴─────────┐ |
| 60 | │ │ │ |
| 61 | App.DTO App.BLL App.DAL.EF |
| 62 | ▲ ▲ ▲ |
| 63 | │ │ │ |
| 64 | └────────┴──── WebApp ─────┘ |
| 65 | (Composition Root) |
| 66 | ``` |
| 67 | |
| 68 | **Tähelepanu**: `App.BLL` **ei viita** `App.DAL.EF`-le. BLL näeb ainult liideseid (`IAppUnitOfWork`), mis elavad Domain-is. See ongi Onion-i **"Dependency Inversion"** — täpselt nagu loengus kirjeldatud. |
| 69 | |
| 70 | --- |
| 71 | |
| 72 | ## 3. Võrdlus TalTech-i loengu nõuetega |
| 73 | |
| 74 | | TalTech-i Clean/Onion nõue | SplitApp teostus | Vastab? | |
| 75 | |---|---|---| |
| 76 | | **"Who owns the interfaces"** → liidesed elavad Domain-is | `IAppUnitOfWork`, `ITripRepository` jt asuvad [App.Domain/Contracts/](SplitApp/App.Domain/Contracts/) | Jah | |
| 77 | | **Entities in Domain** (mitte DAL-is) | Kõik entiteedid [App.Domain/](SplitApp/App.Domain/), POCO, ilma EF-atribuutideta | Jah | |
| 78 | | **Infrastructure → Domain** (pöördsuund) | `App.DAL.EF` viitab `App.Domain`-le, mitte vastupidi | Jah | |
| 79 | | **Application ← Web, Application → Domain** | `WebApp → App.BLL → App.Domain` | Jah | |
| 80 | | **Infrastructure pluginatav** | BLL kasutab ainult liideseid; EF-i saaks teoreetiliselt Dapperi vastu vahetada | Jah | |
| 81 | | **Repository pattern** | [BaseRepository.cs](SplitApp/App.DAL.EF/Repositories/BaseRepository.cs) + spetsiifilised repod | Jah | |
| 82 | | **Unit of Work** (`SaveChangesAsync()` atomic commit) | [AppUnitOfWork.cs](SplitApp/App.DAL.EF/AppUnitOfWork.cs), lazy-load repod | Jah | |
| 83 | | **DTOs** ("dumb objects, no logic") | [App.DTO/v1/](SplitApp/App.DTO/v1/) + Mappers | Jah | |
| 84 | | **Mappers** entiteet↔DTO | Staatilised mapper-klassid [App.DTO/Mappers/](SplitApp/App.DTO/Mappers/) | Jah | |
| 85 | | **Dependency Injection** (konstruktori kaudu) | Teenused `AddScoped`-ina Program.cs-is, konstruktori-injection | Jah | |
| 86 | | **DI Composition Root** välimises ringis | Ainult [Program.cs](SplitApp/WebApp/Program.cs) teab konkreetseid implementatsioone | Jah | |
| 87 | | **SOLID → DIP**: kõrgemad kihid sõltuvad abstraktsioonidest | BLL sõltub `IAppUnitOfWork`-ist, mitte `AppUnitOfWork`-ist | Jah | |
| 88 | | **ViewModelid** (mitte entiteedid vaadetes) | [WebApp/Models/](SplitApp/WebApp/Models/) sisaldab ViewModel-eid | Jah | |
| 89 | |
| 90 | --- |
| 91 | |
| 92 | ## 4. Lisaväärtused üle loengu miinimumi |
| 93 | |
| 94 | - **Versioneeritud API** (`[ApiVersion("1.0")]`, `api/v{version}/...`) — Asp.Versioning |
| 95 | - **JWT + Cookie hübriid-autentimine** — MVC ja REST samaaegselt |
| 96 | - **Admin Area** eraldi teenustega (12 AdminService-t) |
| 97 | - **Lokaliseerimine** läbi `LangStr` (Domain-is) + .resx ([App.Resources/](SplitApp/App.Resources/)) |
| 98 | - **DataProtection** võtmed DB-s (`PersistKeysToDbContext`) |
| 99 | - **PostgreSQL + Docker** tootmiseks |
| 100 | - **Swagger** versioonidega ([ConfigureSwaggerOptions.cs](SplitApp/WebApp/ConfigureSwaggerOptions.cs)) |
| 101 | |
| 102 | --- |
| 103 | |
| 104 | ## 5. Projektide detailne kirjeldus |
| 105 | |
| 106 | ### Sisemine südamik |
| 107 | |
| 108 | **[Base.Domain](SplitApp/Base.Domain/)** — `BaseEntity` (Guid Id, CreatedAt, UpdatedAt), `LangStr` (mitmekeelne string, implitsiitne cast `string ↔ LangStr`). |
| 109 | |
| 110 | **[Base.Contracts](SplitApp/Base.Contracts/)** — generic abstraktsioonid: |
| 111 | - `IBaseRepository<T>` — CRUD (GetAllAsync, GetByIdAsync, Add, Update, RemoveAsync, ExistsAsync) |
| 112 | - `IUnitOfWork` — `SaveChangesAsync()` |
| 113 | - `IBaseEntity` |
| 114 | |
| 115 | **[App.Domain](SplitApp/App.Domain/)** — puhas äridomeen, POCO entiteedid: |
| 116 | - **Trip, Expense, ExpenseSplit** — reisid ja kulud |
| 117 | - **TripParticipant, TripInvitation** — osavõtjad ja kutsed |
| 118 | - **SettlementPlan, SettlementPayment** — arveldused |
| 119 | - **TripPoll, TripPollOption, TripPollVote** — küsitlused |
| 120 | - **TripWishlistItem, TripWishlistVote** — sooviste nimekiri |
| 121 | - **BudgetCategory, Currency, SplitPreset** — abi-entiteedid |
| 122 | - **Identity/** — `AppUser`, `AppRole` |
| 123 | - **Contracts/** — `IAppUnitOfWork` + repo-liidesed (`ITripRepository` jne) |
| 124 | - **Enumid**: `ETripStatus`, `EParticipantRole`, `ESplitMethod`, `ESettlementStatus`, `EPaymentStatus`, `EInvitationStatus`, `EWishlistPriority`, `EWishlistCategory` |
| 125 | |
| 126 | ### Infrastructure — DAL |
| 127 | |
| 128 | **[App.DAL.EF](SplitApp/App.DAL.EF/)** — EF Core + PostgreSQL (Npgsql): |
| 129 | - **[AppDbContext.cs](SplitApp/App.DAL.EF/AppDbContext.cs)** — `IdentityDbContext<AppUser, AppRole, Guid>`, ~13 DbSet, UTC DateTime converter, unique indexid, `DeleteBehavior.Restrict`, `LangStr` JSON-seerimine. |
| 130 | - **[AppUnitOfWork.cs](SplitApp/App.DAL.EF/AppUnitOfWork.cs)** — lazy-load repod, generic `GetRepository<T>()`. |
| 131 | - **[Repositories/](SplitApp/App.DAL.EF/Repositories/)** — `BaseRepository<T>` + spetsiifilised (nt `TripRepository` `GetUserTripsAsync`, `GetByIdWithDetailsAsync`). |
| 132 | - **Migrations/** — EF Core migreeringud. |
| 133 | - **Seeding/** — `InitialData.cs`, `AppDataInit.cs`. |
| 134 | - **[ServiceCollectionExtensions.cs](SplitApp/App.DAL.EF/ServiceCollectionExtensions.cs)** — `AddDalServices()` DI registreerimine, `NoTrackingWithIdentityResolution`, SplitQuery käitumine. |
| 135 | |
| 136 | ### Application Layer — BLL |
| 137 | |
| 138 | **[App.BLL](SplitApp/App.BLL/)** — ärieteenused, sõltub **ainult** Domain-ist (mitte DAL-ist otse): |
| 139 | - **Services**: `TripService`, `ExpenseService`, `SettlementService`, `InvitationService`, `PollService`, `WishlistService`, `BudgetCategoryService`, `SplitPresetService` |
| 140 | - **Admin services** — 12 eraldi admin teenust (`BudgetCategoryAdminService`, `CurrencyAdminService` jne) |
| 141 | - **Helpers/** — `CurrencyConverter` jt |
| 142 | |
| 143 | ### DTO kiht |
| 144 | |
| 145 | **[App.DTO](SplitApp/App.DTO/)** — API- ja teenuste-tasemel andmeobjektid: |
| 146 | - **v1/** — versioneeritud API DTO-d (`TripDto`, `TripCreateDto`, `TripUpdateDto`, `ExpenseDto`, `SettlementPlanDto`, `BudgetCategoryDto`) |
| 147 | - **v1/Identity/** — `LoginInfo`, `RegisterInfo`, `JWTResponse`, `TokenRefreshInfo` |
| 148 | - **Mappers/** — staatilised mapper-klassid (`TripMapper` jt) |
| 149 | |
| 150 | ### Presentation — WebApp |
| 151 | |
| 152 | **MVC Controllers** [Controllers/](SplitApp/WebApp/Controllers/) — Razor Views: |
| 153 | `TripsController`, `ExpensesController`, `BudgetController`, `MembersController`, `PollsClientController`, `WishlistClientController`, `SettlementController` |
| 154 | |
| 155 | **API Controllers** [ApiControllers/](SplitApp/WebApp/ApiControllers/) — REST API: |
| 156 | - `[ApiVersion("1.0")]` + `api/v{version:apiVersion}/[controller]` |
| 157 | - JWT Bearer autentimine |
| 158 | |
| 159 | **Areas**: |
| 160 | - **Admin** — 13 admin-controllerit (Users, Currencies, Trips, Expenses, Polls, Wishlist, SettlementPlans jne) |
| 161 | - **Identity** — Razor Pages (Register jne) |
| 162 | |
| 163 | **Models/** — **ViewModelid** (õppejõu nõue, mitte ViewBag/ViewData) |
| 164 | |
| 165 | **[Program.cs](SplitApp/WebApp/Program.cs)** — DI konfig: |
| 166 | - Identity (`AppUser`, `AppRole`) |
| 167 | - JWT Bearer + Cookie autentimine (SlidingExpiration) |
| 168 | - `AddDataProtection().PersistKeysToDbContext<AppDbContext>()` |
| 169 | - Request localization |
| 170 | - Swagger + API versioning |
| 171 | - `InvariantDecimalModelBinderProvider` — kümnendkoha parser |
| 172 | |
| 173 | --- |
| 174 | |
| 175 | ## Kokkuvõte |
| 176 | |
| 177 | **SplitApp on korrektne Clean/Onion Architecture** täpselt nii, nagu TalTech-i loeng "architecture1" kirjeldab: |
| 178 | |
| 179 | > *"The entire difference between N-tier and Clean Architecture is who owns the interfaces."* |
| 180 | |
| 181 | Meie projektis **liidesed kuuluvad Domain-ile** (`App.Domain.Contracts`), Infrastructure (`App.DAL.EF`) asub välimises ringis ja **osutab sissepoole**, mistõttu BLL-i äriloogika ei tea midagi EF Core-ist ega PostgreSQL-ist. See on just see "pööratud sõltuvus", mida Onion nõuab. |
| 182 | |