Clean architecture i Identity

1

Cześć.
Chciałbym napisać od zera swój pierwszy wielki fullstackowy projekt i chciałbym wykorzystać clean architecture, a jednocześnie trzymać sobie encje usera w bazie.

Niestety, chcac dziedziczyć w Core (AppUser: IdentityUser) wprowadzam dependency na pakiecie Identity od MS - co jest wbrew założeniom architektury.

Ewentualnie jak odnieść się do encji user który może mieć n czegoś tam.

Jak podchodzicie do tego problemu? Chętnie posłucham rady kogoś bardziej doświadczonego.

1

Korzystacie z Identity?

Możesz korzystać z identity i nie dziedziczyć z Identityuser

2

Ja bym w Core zrobił sobie jakąś reprezentację usera, która jest całkowicie niezależna od ASP.NET Core Identity (bo domeną mojej aplikacji raczej nie jest zarządzanie hasłami użytkownika, czy tam opcjami MFA, więc domena może zupełnie o tym nie wiedzieć ;)).
Powiązanie pomiędzy encją w Core, a obiektem IdentityUser z ASP.NET Core Identity można zrobić luźno na podstawie Id/Guid.

Do Core wstrzykuję jakiś IUserContext mający w sobie identyfikator użytkownika (albo jakiś worek z propertisami jak potrzebuję czegoś więcej) i później w zależności w jakim kontekście operuję w Core, pobieram na podstawie tego identyfikatora odpowiednią encję biznesowo reprezentującą użytkownika.
UserContext wypełniany jest danymi pochodzącymi czy to z jakiejś sesji czy z JWT, w zależności czego tam akurat fizycznie używam do uwierzytelnienia, ale dzieje się to poza Core, bo nie jest to domena mojej aplikacji (ja często to robiłem po prostu w warstwie webowej, bo tam obsługiwałem sesje/JWT, ale nie wiem czy jest to zgodnie z czystą architekturą :]).

A samo zarządzanie użytkownikami też lubię wynieść do zewnętrznej usługi :) Jakieś Azure AD, Azure AD B2C, Auth0, czy IdentityServer, w zależności od przypadku.

Z czystym ASP.NET Core Identity ostatnio do czynienia miałem kilka lat temu, ale do obsługi logowania/rejestracji pewnie zrobiłbym sobie jakiś oddzielny moduł Auth poza Core.

0
rjakubowski napisał(a):

Cześć.
Chciałbym napisać od zera swój pierwszy wielki fullstackowy projekt i chciałbym wykorzystać clean architecture, a jednocześnie trzymać sobie encje usera w bazie.

Niestety, chcac dziedziczyć w Core (AppUser: IdentityUser) wprowadzam dependency na pakiecie Identity od MS - co jest wbrew założeniom architektury.

Ewentualnie jak odnieść się do encji user który może mieć n czegoś tam.

Jak podchodzicie do tego problemu? Chętnie posłucham rady kogoś bardziej doświadczonego.

Obejrzyj:

Bez wiedzy o elementach zawartych w tych filmach nawet nie masz co marzyć o "clean architecture".

0

Musisz zrobić sobie interfejs np. IIdentityService, który będzie zależnością Twojego Core.User.

Później robisz implementację, np. AspNetCoreIdentityService i ta implementacja zależy od biblioteki MS.

Edit po dyskusji z @Riddle

Klasa Core.User nie wie nic o autentykacji, autoryzacji i nie odpowiada za to.

Autentykacja, autoryzacja powinny być w warstwie aplikacji.

Musi tak być, poza tym jeżeli planujesz w aplikacji jakieś role/uprawnienia np. UserPrivilege użytkowników to User byłby od nich zależny od nich "w bok", a to nie jest zgodne z założeniami clean architecture.

0
Kamil A napisał(a):

Musisz zrobić sobie interfejs np. IIdentityService, który będzie zależnością Twojego Core.User.

Później robisz implementację, np. AspNetCoreIdentityService i ta implementacja zależy od biblioteki MS.

No takie 3/10 rozwiązanie powiedziałbym.

Co z tego że to jest interfejs, jak i tak user musi wiedzieć że to jest identity service? Równie dobrze mógłbyś przekazać klasę service i na to samo by wyszło.

0

Nie mógł bym, bo wciągnął bym w ten sposób zależność. Interfejs nie ma zależności. Na tym właśnie to polega. Jest elementem odprzęgającym.

0
Kamil A napisał(a):

Nie mógł bym, bo wciągnął bym w ten sposób zależność. Interfejs nie ma zależności. Na tym właśnie to polega. Jest elementem odprzęgającym.

Przekazujac interfejs IIdentityService również wciągasz.

Jestem za tym, żeby oddzielić jakoś AspNetCoreIdentityService od Core.User, ale na pewno interfejs IIdentityService to nie jest krok w dobrą stronę. Z punktu widzenia dependency inversion, to taki interfejs nie jest żadnym usprawnieniem.

0
Riddle napisał(a):

Przekazujac interfejs IIdentityService również wciągasz.

Nie wciągam:

namespace Blabla.Common.Interfaces;
public interface IIdentityService
{
    string GetUserName(string userId);
    bool HasRole(string userId, string role);
    bool Authorize(string userId, string policyName);
    bool CreateUser(string userName, string password);
    bool DeleteUser(string userId);
    // itd itp
}
0
Kamil A napisał(a):
Riddle napisał(a):

Przekazujac interfejs IIdentityService również wciągasz.

Nie wciągam:

namespace Blabla.Common.Interfaces;

public interface IIdentityService
{
    string GetUserName(string userId);
    bool HasRole(string userId, string role);
    bool Authorize(string userId, string policyName);
    bool CreateUser(string userName, string password);
    bool DeleteUser(string userId);
    // itd itp
}

No tak, i ten interfejs wcale nie przenika szczegółów do User.Core.

Widzę że nie za bardzo rozumiesz co mam na myśli, więc może skończmy temat. Prezentujesz postawę "jeśli tylko wydzielę interfejs, to te dwie rzeczy są odseparowane", to wymagałoby pewnie paru godzin debaty żeby zaprezentować przykłady czemu to nie zawsze jest prawda. Także z mojej strony EOT.

0 użytkowników online, w tym zalogowanych: 0, gości: 0