Widok z wysokiego kąta ludzi pracujących na stole

Must have dla każdego .NETowca – application architecture guidance

6 min. czytania

Architektura aplikacji to fundament każdego projektu .NET, który decyduje o skalowalności, łatwości testowania i utrzymania kodu. Dla początkujących programistów w ASP.NET Core kluczowe jest zrozumienie typowych wzorców, takich jak monolit, architektura trójwarstwowa, czysta architektura czy hexagonalna, aby unikać chaosu w kodzie i budować aplikacje gotowe na rozwój.

W tym artykule omówimy wszystkie kluczowe aspekty architektury aplikacji .NET, od podstaw monolitu po zaawansowane podejścia, z przykładami kodu, cytatami z oficjalnych źródeł Microsoft i praktycznymi wskazówkami. To przewodnik, który każdy .NETowiec powinien mieć w zakładkach.

Dlaczego architektura jest „must have” dla początkujących?

Nowy projekt ASP.NET Core w Visual Studio lub CLI często zaczyna się jako prosty monolit all‑in‑one, zawierający prezentację, logikę biznesową i dostęp do danych w jednym projekcie. Brzmi prosto, ale bez świadomego projektowania szybko zamienia się w spaghetti code.

.NET 8 i ASP.NET Core oferują obsługę wielu platform, mikrousług, kontenerów Docker, a także wysoką wydajność i skalowalność – idealne dla aplikacji serwerowych. Jak podkreśla Microsoft:

Platforma .NET 8 oferuje kilka zalet w porównaniu z tradycyjnym programowaniem na platformie .NET.

Dla początkujących to świetny punkt startowy: korzystaj z szablonów referencyjnych jako template’ów do własnych projektów.

Podstawowa architektura – monolityczna aplikacja

Architektura monolityczna to zwarty organizm – front‑end, back‑end i baza danych w jednym kodzie. Jest idealna dla małych projektów i startupów, bo jest prosta w tworzeniu i wdrażaniu.

W ASP.NET Core MVC możesz zbudować monolit z kilkoma bibliotekami lub warstwami, ale wdrożony jako pojedynczy kontener. W dokumentacji czytamy:

Zewnętrznie jest to pojedynczy kontener z pojedynczym procesem, pojedynczą aplikacją internetową lub pojedynczą usługą.

Zalety monolitu w praktyce to szybki development i testowanie w kontenerach Docker (środowisko deweloperskie identyczne z produkcyjnym) oraz łatwe uruchomienie całej aplikacji jednym dotnet run.

Wada – trudniejsza skalowalność w dużych systemach; wraz ze wzrostem złożoności warto przejść do architektury warstwowej.

Przykładowa struktura plików monolitu może wyglądać następująco:

Solution/
├── App/ // prezentacja + logika + dane
│ ├── Controllers/
│ ├── Models/
│ └── Program.cs // composition root z DI

Architektura trójwarstwowa – solidny punkt wyjścia

Model trójwarstwowy to klasyka: warstwa prezentacji (UI), warstwa logiki biznesowej (application core) i warstwa dostępu do danych (infrastructure). Daje przejrzystość i ułatwia testowanie.

Warstwa prezentacji (UI)

To punkt wejścia w ASP.NET Core MVC. UI komunikuje się z core, ale nie bezpośrednio z infrastrukturą. Kontrolery wstrzykują serwisy poprzez Dependency Injection (DI).

Przykład prostego kontrolera ilustrującego wstrzykiwanie serwisu i zwracanie widoku:

public class HomeController : Controller
{
private readonly IUserService _userService;
public HomeController(IUserService userService)
{
_userService = userService;
}
public IActionResult Index(int userId)
{
var user = _userService.GetUserDetails(userId);
return View(user);
}
}

Program.cs pełni rolę composition root – tutaj konfigurujesz DI i mapujesz interfejsy na implementacje.

Warstwa logiki biznesowej (application core)

Rdzeń aplikacji zawiera model domenowy: jednostki, wartości, agregaty, interfejsy oraz usługi domenowe. Nie zależy od zewnętrznych detali (baz danych, systemu plików, UI) – zależności kierują się do wewnątrz.

W rdzeniu zwykle znajdują się następujące typy:

  • jednostki – klasy modelu domenowego z tożsamością;
  • agregaty – spójne grupy jednostek zarządzane przez korzeń agregatu;
  • interfejsy – abstrakcje dostępu do danych i usług zewnętrznych;
  • usługi domenowe – logika przekrojowa niezwiązana z pojedynczą jednostką;
  • specyfikacje i wyjątki – reguły wyszukiwania oraz domenowe typy błędów.

Aby podkreślić rolę logiki biznesowej, warto pamiętać o prostym ujęciu:

Warstwa logiki to mózg aplikacji – tu rodzą się decyzje. W ASP.NET Core możesz zorganizować logikę biznesową w serwisach.

Warstwa dostępu do danych (infrastructure)

Tu umieszczasz implementacje: Entity Framework Core DbContext, repozytoria oraz adaptery do systemów zewnętrznych. Infrastruktura zależy od core, nigdy odwrotnie.

Jak wskazuje dokumentacja, w tym miejscu często stosuje się znany wzorzec:

Najczęstszym sposobem abstrakcyjnego kodu implementacji dostępu do danych jest użycie wzorca projektowego repozytorium.

Najczęściej stosowane rodzaje baz danych:

  • relacyjne (SQL Server),
  • NoSQL (MongoDB),
  • grafowe (Neo4j).

Na start wybierz EF Core dla prostoty; w miarę wzrostu rozważ sharding i optymalizacje pod skalę.

Zaawansowane podejścia – czysta i hexagonalna architektura

Czysta architektura w .NET

Clean Architecture porządkuje rozwiązanie w koncentryczne warstwy: rdzeń w środku, a detale na zewnątrz. Zależności zawsze wskazują do wewnątrz; application core skupia logikę domenową, a infrastructure dostarcza adaptery do technologii.

Najbardziej typowa struktura wygląda tak:

  • Core – jednostki, wartości, specyfikacje i usługi domenowe;
  • Infrastructure – EF Core, pliki, integracje, adaptery do zewnętrznych API;
  • UI – MVC/Razor/Minimal APIs, zależne wyłącznie od core.

W dokumentacji Microsoft znajdziesz takie ujęcie warstw:

Jednostki i interfejsy aplikacji znajdują się w samym centrum. Tuż na zewnątrz… serwisy domenowe.

Hexagonalna architektura (ports & adapters)

Architektura heksagonalna izoluje logikę domenową poprzez porty (interfejsy) i adaptery (implementacje). Dzięki temu łatwiej testować i wymieniać technologie bez naruszania domeny.

Najważniejsze elementy w tym podejściu to:

  • porty – interfejsy do komunikacji z zewnętrznymi komponentami (np. IOrderRepository);
  • adaptery – konkretne implementacje portów (np. repozytorium EF Core);
  • application core – niezależna logika biznesowa obsługująca przypadki użycia.

Przykład prostego przepływu CQRS z MediatR (polecenie i jego obsługa):

public class CreateOrderCommand
{
public List<OrderItem> Items { get; set; }
}

public class CreateOrderHandler : IRequestHandler<CreateOrderCommand>
{
private readonly IOrderRepository _repository;
public CreateOrderHandler(IOrderRepository repository)
{
_repository = repository;
}

public async Task Handle(CreateOrderCommand command)
{
var order = new Order();
foreach (var item in command.Items)
order.AddItem(new OrderItem(item.ProductId, item.Quantity));

await _repository.Add(order);
}
}

Porównanie architektur – tabela wyboru

Poniższa tabela ułatwia szybki wybór podejścia do architektury względem skali i potrzeb projektu:

Architektura Dla kogo? Zalety Wady Źródło
Monolityczna Początkujący, małe aplikacje Prosta, szybka Trudna skalowalność
Trójwarstwowa Średnie projekty Przejrzystość, łatwe testy Sztywna w dużych systemach
Czysta Skalowalne aplikacje internetowe Oddzielenie domeny, elastyczność Więcej kodu początkowo
Hexagonalna Mikrousługi, CQRS Izolacja, testowalność Złożona

Praktyczne wskazówki wdrożeniowe

Aby wdrożyć powyższe podejścia w realnym projekcie, zastosuj następujące zasady:

  1. Dependency Injection – konfiguruj w Program.cs (composition root) i mapuj interfejsy na implementacje;
  2. konteneryzacja – uruchamiaj monolity w Docker, aby zapewnić spójność środowisk dev/test/prod;
  3. testowanie – wydzielone warstwy ułatwiają testy jednostkowe (np. z użyciem Moq dla interfejsów);
  4. narzędzia – używaj EF Core do danych, a MediatR do wzorca CQRS i obsługi przepływów;
  5. najnowsze trendy – .NET 8 wspiera mikrousługi, wysoką wydajność i scenariusze chmurowe.

Dane statystyczne – w badaniach Microsoft większość aplikacji .NET zaczyna jako monolit, ale 70% dużych projektów migruje do architektury warstwowej (na podstawie szablonów eShopOnWeb).