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

Clean / Onion Architecture — Project Layer Mapping

This document maps the 9 csproj projects in SplitApp/ to the Onion architecture rings as taught in the TalTech lecture (courses.taltech.akaver.com/web-applications-with-csharp/lectures/architecture1).


The 9 projects

SplitApp/
├── Base.Contracts/         ← Domain Contracts (generic / cross-cutting, innermost)
├── Base.Domain/            ← Domain Primitives (cross-cutting)
├── Base.Helpers/           ← Infrastructure (cross-cutting helpers)
├── App.Domain/             ← Domain Layer (CORE)
├── App.DAL.EF/             ← Infrastructure (Data Access Layer)
├── App.BLL/                ← Application Services / Business Logic Layer
├── App.DTO/                ← Presentation Contracts (public API DTOs)
├── App.Resources/          ← Infrastructure (i18n / localization)
└── WebApp/                 ← Presentation / UI (MVC + REST + Composition Root)

Onion Rings (innermost → outermost)

Ring 1 — Domain Model (CORE)

The innermost ring. Knows nothing about EF, ASP.NET, JWT, or any other technology.

Project Lecture name Contents
Base.Contracts Domain Contracts (generic / base) IBaseEntity.cs, IBaseRepository.cs, IUnitOfWork.cs
Base.Domain Domain Primitives / base entity classes BaseEntity.cs (Id, CreatedAt, UpdatedAt), LangStr.cs (i18n value object)
App.Domain Domain Entities + Domain Contracts (app-specific) All concrete entities (Trip.cs, Expense, BudgetCategory, Currency, TripPoll, ...), enums (ETripStatus, EParticipantRole, ...), Identity/ (AppUser, AppRole, AppRefreshToken), and App.Domain/Contracts/ with IAppUnitOfWork, ITripRepository, IExpenseRepository, etc.

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.


Ring 2 — Application / BLL (Business Logic Layer)

Orchestrates use cases. Talks to the Domain only through the repository contracts in App.Domain/Contracts/.

Project Lecture name Contents
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))

Ring 3 — Infrastructure

Implements the contracts from the inner rings using concrete technologies (EF Core, JWT library, .NET resx).

Project Lecture name Contents
App.DAL.EF Infrastructure — Data Access Layer (EF Core implementation) AppDbContext.cs (EF DbContext, IdentityDbContext), AppUnitOfWork.cs (implements IAppUnitOfWork), Repositories/* (concrete repos implementing the interfaces from App.Domain/Contracts/), Migrations/, Seeding/, ServiceCollectionExtensions.cs (AddDalServices), UtcDateTimeConverter.cs
App.Resources Infrastructure — i18n / Localization resources Domain/*.resx + *.et.resx, Common.resx, Views/Shared.resx, Domain/Enums.resx
Base.Helpers Infrastructure — cross-cutting helpers IdentityHelpers.cs (JWT generate/validate using System.IdentityModel.Tokens.Jwt)

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.


Ring 4 — Presentation / UI (outermost)

The composition root. Wires everything together.

Project Lecture name Contents
App.DTO Presentation Contracts (public API DTOs) v1/TripDto.cs, v1/TripCreateDto.cs, v1/TripUpdateDto.cs, v1/Identity/* (LoginInfo, RegisterInfo, TokenRefreshInfo)
WebApp Presentation / UI (MVC + REST API + Composition Root) 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, Helpers/

Dependency direction (must flow inward only)

WebApp ──────┐
             ├──► App.BLL ──► App.Domain ──► Base.Domain ──► Base.Contracts
App.DTO ─────┘                  ▲
                                │ (App.Domain/Contracts/ITripRepository etc.)
App.DAL.EF ─────────────────────┘  implements those contracts
App.Resources, Base.Helpers ─── leaf utilities, used by outer rings

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.


What "Infrastructure" means in this project

In the lecture's vocabulary, Infrastructure = anything that talks to the outside world or to a specific technology. In this project that is:

  1. App.DAL.EF — primary infrastructure (EF Core, Postgres, migrations).
  2. App.Resources — secondary infrastructure (resx files for the .NET localization framework).
  3. Base.Helpers — JWT token plumbing (depends on System.IdentityModel.Tokens.Jwt, which is a tech-specific library).

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?".


Three-tier DTO flow

[ DB row ]
    │  EF Core
    ▼
App.Domain.Trip                ◄── domain entity (innermost)
    │  TripBllDtoFactory.Create()  in App.BLL/Mappers/
    ▼
App.BLL.DTO.TripBllDto         ◄── BLL-internal DTO
    │  inline mapping in WebApp/ApiControllers/TripsController.cs
    ▼
App.DTO.v1.TripDto             ◄── public REST DTO (the wire format)
    │  JSON
    ▼
[ HTTP response ]

Summary table — at a glance

Onion ring Lecture name Project(s)
1 (innermost) Domain Contracts (generic) Base.Contracts
1 Domain Primitives Base.Domain
1 Domain Entities + app-specific Domain Contracts App.Domain (incl. App.Domain/Contracts/)
2 Application Services / BLL App.BLL
3 Infrastructure — DAL App.DAL.EF
3 Infrastructure — i18n App.Resources
3 Infrastructure — cross-cutting helpers Base.Helpers
4 Presentation Contracts (public DTOs) App.DTO
4 (outermost) Presentation / UI + Composition Root WebApp