profileShare

rasmusjy / splitapp-backend-clean-onion

Read-only snapshot

No repository description.

main default branch 429 files Expires Sep 13, 2026, 9:06 AM
architecture.md 7,969 bytes
1 # Clean / Onion Architecture — Project Layer Mapping
2
3 This document maps the 9 csproj projects in [SplitApp/](SplitApp/) to the Onion architecture rings as taught in the TalTech lecture (`courses.taltech.akaver.com/web-applications-with-csharp/lectures/architecture1`).
4
5 ---
6
7 ## The 9 projects
8
9 ```
10 SplitApp/
11 ├── Base.Contracts/ ← Domain Contracts (generic / cross-cutting, innermost)
12 ├── Base.Domain/ ← Domain Primitives (cross-cutting)
13 ├── Base.Helpers/ ← Infrastructure (cross-cutting helpers)
14 ├── App.Domain/ ← Domain Layer (CORE)
15 ├── App.DAL.EF/ ← Infrastructure (Data Access Layer)
16 ├── App.BLL/ ← Application Services / Business Logic Layer
17 ├── App.DTO/ ← Presentation Contracts (public API DTOs)
18 ├── App.Resources/ ← Infrastructure (i18n / localization)
19 └── WebApp/ ← Presentation / UI (MVC + REST + Composition Root)
20 ```
21
22 ---
23
24 ## Onion Rings (innermost → outermost)
25
26 ### Ring 1 — Domain Model (CORE)
27
28 The innermost ring. Knows nothing about EF, ASP.NET, JWT, or any other technology.
29
30 | Project | Lecture name | Contents |
31 |---|---|---|
32 | **`Base.Contracts`** | **Domain Contracts (generic / base)** | [IBaseEntity.cs](SplitApp/Base.Contracts/IBaseEntity.cs), [IBaseRepository.cs](SplitApp/Base.Contracts/IBaseRepository.cs), [IUnitOfWork.cs](SplitApp/Base.Contracts/IUnitOfWork.cs) |
33 | **`Base.Domain`** | **Domain Primitives / base entity classes** | [BaseEntity.cs](SplitApp/Base.Domain/BaseEntity.cs) (Id, CreatedAt, UpdatedAt), [LangStr.cs](SplitApp/Base.Domain/LangStr.cs) (i18n value object) |
34 | **`App.Domain`** | **Domain Entities + Domain Contracts (app-specific)** | All concrete entities ([Trip.cs](SplitApp/App.Domain/Trip.cs), Expense, BudgetCategory, Currency, TripPoll, ...), enums (`ETripStatus`, `EParticipantRole`, ...), `Identity/` (AppUser, AppRole, AppRefreshToken), and `App.Domain/Contracts/` with `IAppUnitOfWork`, `ITripRepository`, `IExpenseRepository`, etc. |
35
36 **Key rule:** Repository interfaces (`ITripRepository`, `IAppUnitOfWork`) live in `App.Domain/Contracts/`, **not** in the DAL. That is the classical Onion rule — the inner ring defines the contract, the outer ring implements it, so dependencies always point inward.
37
38 ---
39
40 ### Ring 2 — Application / BLL (Business Logic Layer)
41
42 Orchestrates use cases. Talks to the Domain only through the repository contracts in `App.Domain/Contracts/`.
43
44 | Project | Lecture name | Contents |
45 |---|---|---|
46 | **`App.BLL`** | **Application Services / Business Logic Layer (BLL)** | `Services/` (`ITripService` + `TripService`, `IExpenseService`, `IInvitationService`, ...), `Services/Admin/` (admin services), `Services/Identity/` (`IIdentityService` — JWT issuing, refresh tokens), `DTO/` (`TripBllDto`, `ExpenseBllDto`, `BalanceBllDto`, ...), `Mappers/` (`TripBllDtoFactory.Create(entity)` and `.ToEntity(dto)`) |
47
48 ---
49
50 ### Ring 3 — Infrastructure
51
52 Implements the contracts from the inner rings using concrete technologies (EF Core, JWT library, .NET resx).
53
54 | Project | Lecture name | Contents |
55 |---|---|---|
56 | **`App.DAL.EF`** | **Infrastructure — Data Access Layer (EF Core implementation)** | [AppDbContext.cs](SplitApp/App.DAL.EF/AppDbContext.cs) (EF DbContext, IdentityDbContext), [AppUnitOfWork.cs](SplitApp/App.DAL.EF/AppUnitOfWork.cs) (implements `IAppUnitOfWork`), `Repositories/*` (concrete repos implementing the interfaces from `App.Domain/Contracts/`), `Migrations/`, `Seeding/`, [ServiceCollectionExtensions.cs](SplitApp/App.DAL.EF/ServiceCollectionExtensions.cs) (`AddDalServices`), `UtcDateTimeConverter.cs` |
57 | **`App.Resources`** | **Infrastructure — i18n / Localization resources** | `Domain/*.resx` + `*.et.resx`, `Common.resx`, `Views/Shared.resx`, `Domain/Enums.resx` |
58 | **`Base.Helpers`** | **Infrastructure — cross-cutting helpers** | [IdentityHelpers.cs](SplitApp/Base.Helpers/IdentityHelpers.cs) (JWT generate/validate using `System.IdentityModel.Tokens.Jwt`) |
59
60 **Note on DAL contracts placement:** Akaver sometimes splits this into `App.DAL.Contracts` (interfaces) + `App.DAL.EF` (implementation). In this project the DAL contracts are inlined into `App.Domain/Contracts/`, which is also valid Onion — and arguably purer, because the contracts live in the Domain ring rather than in a separate "DAL contracts" project.
61
62 ---
63
64 ### Ring 4 — Presentation / UI (outermost)
65
66 The composition root. Wires everything together.
67
68 | Project | Lecture name | Contents |
69 |---|---|---|
70 | **`App.DTO`** | **Presentation Contracts (public API DTOs)** | `v1/TripDto.cs`, `v1/TripCreateDto.cs`, `v1/TripUpdateDto.cs`, `v1/Identity/*` (LoginInfo, RegisterInfo, TokenRefreshInfo) |
71 | **`WebApp`** | **Presentation / UI** (MVC + REST API + Composition Root) | [Program.cs](SplitApp/WebApp/Program.cs) (composition root), `ApiControllers/` (versioned REST), `Controllers/` (client MVC), `Areas/Admin/Controllers/` + `Areas/Admin/Views/` (admin MVC area), `Areas/Identity/` (scaffolded Identity UI), `Views/`, [ConfigureSwaggerOptions.cs](SplitApp/WebApp/ConfigureSwaggerOptions.cs), `Helpers/` |
72
73 ---
74
75 ## Dependency direction (must flow inward only)
76
77 ```
78 WebApp ──────┐
79 ├──► App.BLL ──► App.Domain ──► Base.Domain ──► Base.Contracts
80 App.DTO ─────┘ ▲
81 │ (App.Domain/Contracts/ITripRepository etc.)
82 App.DAL.EF ─────────────────────┘ implements those contracts
83 App.Resources, Base.Helpers ─── leaf utilities, used by outer rings
84 ```
85
86 **Verified in code:** `App.DAL.EF` references `App.Domain` (so it can implement `ITripRepository`), but `App.Domain` does **not** reference `App.DAL.EF`. That is correct.
87
88 ---
89
90 ## What "Infrastructure" means in this project
91
92 In the lecture's vocabulary, **Infrastructure = anything that talks to the outside world or to a specific technology**. In this project that is:
93
94 1. **`App.DAL.EF`** — primary infrastructure (EF Core, Postgres, migrations).
95 2. **`App.Resources`** — secondary infrastructure (resx files for the .NET localization framework).
96 3. **`Base.Helpers`** — JWT token plumbing (depends on `System.IdentityModel.Tokens.Jwt`, which is a tech-specific library).
97
98 Everything in those three projects can be swapped (e.g. switch from EF to Dapper, from resx to a translations DB, from JWT to OAuth) without touching `App.Domain` or `App.BLL`. That is the test of "is it infrastructure?".
99
100 ---
101
102 ## Three-tier DTO flow
103
104 ```
105 [ DB row ]
106 │ EF Core
107
108 App.Domain.Trip ◄── domain entity (innermost)
109 │ TripBllDtoFactory.Create() in App.BLL/Mappers/
110
111 App.BLL.DTO.TripBllDto ◄── BLL-internal DTO
112 │ inline mapping in WebApp/ApiControllers/TripsController.cs
113
114 App.DTO.v1.TripDto ◄── public REST DTO (the wire format)
115 │ JSON
116
117 [ HTTP response ]
118 ```
119
120 - **Entity ↔ BllDto** mapper: [App.BLL/Mappers/TripBllDtoFactory.cs](SplitApp/App.BLL/Mappers/TripBllDtoFactory.cs)
121 - **BllDto ↔ public ApiDto** mapper: currently inlined in the API controllers (no `WebApp/Mappers/` folder exists yet).
122
123 ---
124
125 ## Summary table — at a glance
126
127 | Onion ring | Lecture name | Project(s) |
128 |---|---|---|
129 | 1 (innermost) | **Domain Contracts (generic)** | `Base.Contracts` |
130 | 1 | **Domain Primitives** | `Base.Domain` |
131 | 1 | **Domain Entities + app-specific Domain Contracts** | `App.Domain` (incl. `App.Domain/Contracts/`) |
132 | 2 | **Application Services / BLL** | `App.BLL` |
133 | 3 | **Infrastructure — DAL** | `App.DAL.EF` |
134 | 3 | **Infrastructure — i18n** | `App.Resources` |
135 | 3 | **Infrastructure — cross-cutting helpers** | `Base.Helpers` |
136 | 4 | **Presentation Contracts (public DTOs)** | `App.DTO` |
137 | 4 (outermost) | **Presentation / UI + Composition Root** | `WebApp` |
138