Zaczynasz pierwszy projekt w Blazorze. Rozpoczynanie projektów jest jednym z przyjemniejszych momentów, ja na początku sprawdzam jakie podejścia mogą usprawnić moje projekty oraz jakie są dobre praktyki, a skoro Ty czytasz ten artykuł, to zapewne Ciebie też interesuje ten temat.
W tym poście poruszymy tematy takie jak struktura aplikacji oraz dobre praktyki w budowaniu aplikacji. Na początek struktura solucji, w większości moich projektów wygląda następująco:

Ma wiele wspólnych części z Clean Architecture zaproponowanym przez Jason Taylora, zachęcam do zapoznania się z jego implementacja po dokładniejszy opis poszczególnych projektów, w skrócie ich przeznaczenie to:
Nazwa projektu | Opis |
MyFirstBlazorApp.Api | Projekt zawierający endopinty web api, w tym miejscu znajdować się kontrolery i konfiguracja hosta, auth czy rejestracja serwisów. |
MyFirstBlazorApp.Api.Domain | Domena całej aplikacji, w tym miejscu powinny znajdować się encje, agregaty czy enumy, powinna być całkowicie niezależna od innych warstw. |
MyFirstBlazorApp.Api.Application | Warstwa realizująca logikę aplikacji, tutaj dodamy wszelkiego rodzaju interfejsy, jest to dobre miejsce na logikę związaną z CQRS. Ten wykorzystuje wykorzystuje warstwę domeny w aplikacji. |
MyFirstBlazorApp.Api.Infrastructure | W tym miejscu znajdą się implementacje interfejsów zadeklarowanych w aplikacji, repozytoria, kontekst bazodanowy czy metody rozszerzające. |
MyFirstBlazorApp.Client | Docelowy projekt z komponentami Blazora, w tym miejscu dodamy layouty, komponenty czy konfiguracje części frontendowej. |
MyFirstBlazorApp.Client.Application | Warstwa do logiki aplikacji po stronie frontendu, tak jak w przypadku web api dodajemy tu interfejsy i pozostałą logikę, której użyjemy w Blazorze. |
MyFirstBlazorApp.Client.Infrastructure | Projekt do implementowania interfejsów z warstwy aplikacji, w tym miejscu będą znajdować się serwisy do komunikacji z web api czy obsługi LocalStorage. |
MyFirstBlazorApp.Shared | Warstwa do współdzielenia modeli między web api, a frontendem z Blazorem. |
Wszystkie przedstawione projekty mają swoje odpowiednimi w katalogu z testowymi projektami. Alternatywnie można rozpocząć projekt od utworzenia współdzielonych warstw Application, Domain oraz Infrastructure i zostawić wyłącznie Api i Client jako oddzielne projekty warstwy prezentacji. Ja zdecydowałem się na takie podejście ze względu na to, że nie chciałbym mieszać serwisów wykorzystywanych w Api poprzez część związaną z Blazorem i odwrotnie oraz z doświadczenia wiem, że o wiele łatwiej w przyszłości połączyć kilka projektów w jeden większy niż rozdzielać je na mniejsze.
Teraz pora na strukturę projektu Blazor, u mnie wygląda ona następująco:

Struktura katalogów w projekcie zorientowana jest na funkcjonalności, najważniejsze katalogi prezentują się następująco:
Nazwa katalogu | Opis |
Pages | Katalog z komponentami reprezentującymi strony, oznaczone są dyrektywą @page, składają się z mniejszych komponentów. |
Shared | W tym miejscu przechowywane są layouty oraz współdzielone komponenty. |
Components | Katalog ze strukturą z podziałem na funkcjonalności, jest to miejsce przechowywania wszystkich mniejszych komponentów z których budujemy strony. |
Components/Common | Katalog z komponentami współdzielonymi między poszczególnymi funkcjonalnościami, wewnątrz katalogu jest podział na kontrolki, kontenery oraz elementy layoutów. |
Components/Products | Tutaj będą znajdować się komponenty poszczególnych funkcjonalności, w tym wypadku produktów, możliwe jest dalsze zagnieżdżanie tego katalogu. |
Components/Shared | Katalog z komponentami współdzielonymi między funkcjonalnościami, podobnie jak Common. Różnica między tymi katalogami jest taka, że w Common przechowujemy komponenty, które mogą występować między różnymi projektami np. Button czy Card, natomiast w Shared powinny znajdować się komponenty, które są wykorzystywane w różnych funkcjonalnościach, lecz nie są generyczne np. UserAvatar w przypadku, gdy wyświetlamy użytkownika w kilku różnych miejscach. |
Powyższa struktura jest wynika z moich doświadczeń z projektami frontendowymi, w tym wykorzystującymi Blazora. Trzymanie komponentów najwyższego poziomu oznaczonych dyrektywą @page w katalogu Pages pozwala szybko zorientować się w strukturze projektu, następnie orientacja na funkcjonalności w katalogu Components pozwala na odpowiednie dostosowywanie wielkości podkatalogów i rozbudowywania ich. Ogranicza to także sytuacje w której mamy spore różnice między katalogami, co jest częstym problemem, gdy zdecydujemy się na podział katalogów ze względu na typ.
Ostatnim elementem jest rejestrowanie serwisów. Możemy to zrobić w Program.cs poprzez dopisanie linijki
builder.Services.AddTransient<IService, Service>();
Może to się sprawdzić w przypadku, gdy serwisów jest niewiele, lecz ja zalecam utworzenie w każdym projekcie klasy Module w której będą metody do rejestrowania serwisów, przykładowa implementacja:
public static class InfrastructureModule
{
public static IServiceCollection AddInfrastructure(this IServiceCollection serviceCollection)
{
serviceCollection.AddTransient<IService, Service>();
return serviceCollection;
}
}
Należy jeszcze dodać moduł w Program.cs
builder.Services.AddInfrastructure();
W ten sposób możliwe jest rejestrowanie serwisów w modułach bez potrzeby zmiany Program.cs przy każdej rejestracji, a dodatkowo mamy dokładniejszy podział podczas rejestracji serwisów, a każdy moduł możemy łatwiej przenieść i wykorzystać w innej aplikacji.
Przedstawiona struktura powinna wspomóc tworzenie i utrzymywanie kodu, a także być odporna na rozrastanie się projektu. Pamiętaj, aby pilnować separacji między projektami, wszystkie warstwy mają konkretne przeznaczenie, a trzymanie się ich pozwoli na łatwiejsze poruszanie się po projektach i uniknięcia kosztownych błędów wykorzystywania klas poza ich kontekstem.
Jeżeli uznasz ten post za przydatny to proszę udostępnij go innym, natomiast jak chcesz być na bieżąco i otrzymywać informacje o moich aktywnościach przed innymi to zapraszam Cię do newslettera na którym regularnie udostępniam przedpremierowo artykuły, materiały wideo i wiele więcej.