Clean architecture w Springu

Clean architecture w Springu
AP
  • Rejestracja:około 6 lat
  • Ostatnio:3 miesiące
  • Postów:164
0

Wzoruje się na Clean Architecture i mam kilka wątpliwości.
screenshot-20190803230846.png

Struktura mojego projektu to obecnie
screenshot-20190803231214.png

W domain/user/port mam zdefiniowany interfejs oraz jakiś serwis "biznesowy", który ma mi zwrócić dane personalne uzytkownika

Kopiuj
public interface UserRepository {
    Optional<User> findUser(String username);
}

@RequiredArgsConstructor
public class UserReader {
    private final UserRepository repository;

    public Optional<PersonalDetailsDto> readPersonalData(String username) {
        return repository.findUser(username)
                .map(User::getPersonalDetails)
                .map(PersonalDetailsDto::new);
    }
}

W tej części aplikacji nie używam nic związanego z frameworkiem, tak więc klasy reprezentujące encje są pozbawione adnotacji SpringData

Kopiuj
@Getter
@Builder
public class User {
    private final String username;
    private final String password;
    private final String[] roles;
    private final PersonalDetails personalDetails;
}

Warstwa controllerów, będzie dostawać Optional'a bądź Either'a i jedyne co tu chcę robić to mapować ewentualny Either.left() na odpowiedni kod HTTP. Tak więc DTO z odpowiednim setem danych będzie przygotowywane przy wyjściu z serwisów w warstwie biznesowej.

Kopiuj
@Getter
public class PersonalDetailsDto {
    private final String firstName;
    private final String lastName;
    private final String gender;
    private final String birthDate;
    private final String weight;
    private final String height;

    public PersonalDetailsDto(PersonalDetails personalDetails) {
        this.firstName = personalDetails.getFirstName();
        this.lastName = personalDetails.getFirstName();
        this.gender = personalDetails.getGender().getValue();
        this.birthDate = personalDetails.getBirthDate()
                .format(DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL)
                .withLocale(Locale.UK));
        this.weight = personalDetails.getWeight() + " kg";
        this.height = personalDetails.getHeight() + " cm";
    }
}

Do tej pory ma to sens, mapuje sobie rzeczy jak Gender.MALE na Mężczyzna, czy tam parsuje w różny sposób datę, w zależności od tego co potrzebuje dostać na wyjściu w danym wypadku.

Wątpliwości zaczynam mieć przy warstwie encji bazodanowych.
Obecnie moja implementacja UserRepository wygląda tak

Kopiuj
@Component
public class InMemoryUserRepository implements UserRepository {
    private final Map<String, User> users = new HashMap<>();

    public InMemoryUserRepository() {
        users.put("admin", User.builder()
                .username("admin")
                .password("pass")
                .roles(new String[] {"ADMIN"}).build());

        users.put("user", User.builder()
                .username("user")
                .password("pass")
                .roles(new String[] {"USER"})
                .personalDetails(PersonalDetails.builder()
                        .firstName("John")
                        .lastName("Doe")
                        .gender(Gender.MALE)
                        .height(183.0)
                        .weight(90.0)
                        .birthDate(LocalDate.of(1993, 5, 20))
                .build())
                .build());
    }

    @Override
    public Optional<User> findUser(String username) {
        return Optional.ofNullable(users.get(username));
    }
}

Jednak docelowo, będzie tutaj musiała powstać dodatkowa klasa, reprezentująca użytkownika w bazie danych. Czyli praktycznie kopia tego co mam w warstwie domeny z dodatkowym polem ID + innymi adnotacjami SpringData. Pytanie czy to już nie zaczyna być przerost nad treścią, czy może kompletnie źle zrozumiałem jak ma wyglądać taki podział architektury?

J1
Jest możliwość gdzieś zobaczenia Twoich plików konfiguracyjnych ze spring security? Widze, ze (chyba) pobierasz dane z bazy (użytkownicy), a sam teraz się mocę w tym jak to zrobić.
danek
  • Rejestracja:ponad 10 lat
  • Ostatnio:7 miesięcy
  • Lokalizacja:Poznań
  • Postów:797
1

Generalnie tak, powinny być jakieś obiekty które modelują procesy biznesowe (tak, żeby je ładnie zamodelować), osobne zbiory obiektów jako encje bazodanowe (tak żeby zoptymalizować wykozystanie bazy) i inne obiekty jako dtosy API aplikacji.

Problem jest je przy prostych projektach nie zauważysz różnicy między nimi, bo "domenta" jest zbyt prosta


Spring? Ja tam wole mieć kontrole nad kodem ᕙ(ꔢ)ᕗ
Haste - mała biblioteka do testów z czasem.
hcubyc
warto też dodac, że entitty w clean architecture nie oznacza encji bazodanowej czy czegokolwiek co ma związek z persystencja, a poza tym racja, brakuje w tym przykładowym kodzie 'use cases', bo ja ich nie widze patrząc w kod, tym bardziej, że zamiast jakiegoś normalnego obiektu domenowego są pokazane dtosy
AP
Czyli w skrócie, encja biznesowa nie powinna być prostym workiem na dane, wewnątrz tej klasy powinna już siedzieć logika biznesowa jak np. walidacja czy numer PESEL ma 11 cyfr. Encja bazodanowa to po prostu zbiór pól, powiązanych adnotacjami Springowymi. To powinno rozwiązać problem zbyt prostej domeny. Zastanawia mnie tylko co dokładnie będzie siedzieć w warstwie useCaseów, wiele z nich ograniczy się do próby stworzenia obiektu domenowego, złapanie wyjątku i przekształcenie go na Either'a?
hcubyc
Tak i nie - możesz walidowac w encji, możesz w osobnym obiekcie - nie ma reguły moim zdaniem, ale logika biznesowa jeżeli tylko może siedzi w encji. Generalnie dosłownie nie robię czegoś takiego jak warstwa use casów, ale pamiętaj że muszą też istnieć serwisy - logika powinna siedzieć w encjach, aczkolwiek jest tworzenie encji i np use casy, które wymagają zarządzania kilkoma encjami i to jest odpowiedzialność serwisu
hcubyc
  • Rejestracja:ponad 12 lat
  • Ostatnio:prawie 3 lata
4

Prawie dobrze, widać progres z typowej architektury warstwowej, a teraz musisz zacząć budować moduły, bo w pakiecie entity masz User, Gender i PersonalDetails, co nie łączy się jakiś koncept dla mnie, tym bardziej, że inne rzeczy typu UserRepository, UserReader, UserConfiguration` (kontroler pomine, bo jest w spoko miejscu) sa porozrzucane każdy gdzie indziej. Włóż je w jedno miejsce, stwórz jakieś publiczne API, cała reszta ma byc niedostępna poza API, domenę zacznij wpychać jak najwięcej do obiektów, jak najmniej do serwisów i bedzie ok. Generalnie masz już tam jakieś Secuirty, kontrolery, a nie widać czemu ma to służyć i co ten kod ma robić

Żeby nie zostawić się z niczym - nie zaczynaj od d**y strony. Kompletnie olej Springa. Jednym z założeń Clean Architecture jest to, by kod domenowy (biznesowy) był odseparowany od warstwy aplikacji i infrastruktury, żeby był całkowicie odporny na zmiany w nich. Wymyśl sobie jakąś apke, która ma coś robić, może być to sklep internetowy, forum, whatever. Napisz kod domenowy, który bedzie realizował jakies zadanie, bez Springa. Podejść jest wiele, ja polecam package by component, czyli każdy moduł ładujesz do osobnego pakietu. Jedyne co ma być dostepne publicznie z tego pakietu to jakieś obiekty api, typu dtosy (one mogą być w podpakiecie), jakis punkt wejścia do modułu (wzorzec fasady jest bardzo ok), jakaś klasa, która pozwoli zbudować moduł oraz interfejsy, które w javie muszą byc publiczne żeby mogły być zaimplementowane poza pakietem (generalnie implementacja np. Repository nie powinna być w tym samym pakiecie co kod domenowy). Do tego oczywiście napisz testy, które będą testować wszystkie przypadki użycia tak jakbyś korzystał z tego modułu z zewnątrz, czyli poprzez fasadę czy co innego tam wymyślisz. Klasę User masz niemutowalną, to fajnie, ale encje domenowe wcale nie muszą być niemutowalne - dowolność twórcy. Za to mają gettery na wszystkie pola, czyli nic nie enkapsulują, średnio też z abstrakcją. Niech te obiekty zaczną mieć jakieś zachowania, zamiast pokazywać jakie mają pola. Jak już to zrobisz to kolejną wążną rzeczą jest dodanie kolejnego modułu i komunikacja między nimi. Wszystkie dobre zasady OOP mają też przełożenie na budowanie modułów, np brak cyklicznych zależności. Jak już będziesz miał kilka modułów, logikę biznesową, dobre testy, wtedy tam będziesz mógł dodać Springa, Swinga, implementację repo, która będzie wrzucać do bazy danych.


Limitations are limitless > ##### Ola Nordmann napisał(a)
> Moim językiem ojczystym jest C++ i proszę uszanować to, że piszę po polsku.
edytowany 3x, ostatnio: hcubyc
AP
  • Rejestracja:około 6 lat
  • Ostatnio:3 miesiące
  • Postów:164
0
hcubyc napisał(a):

Prawie dobrze, widać progres z typowej architektury warstwowej, a teraz musisz zacząć budować moduły, bo w pakiecie entity masz User, Gender i PersonalDetails, co nie łączy się jakiś koncept dla mnie,

Kopiuj
@Getter
@Builder
public class User {
    private final String username;
    private final String email;
    private final String password;
    private final String[] roles;
    private final boolean active;
    private final PersonalDetails personalDetails;
}

@Getter
@Builder
public class PersonalDetails {
    private final String firstName;
    private final String lastName;
    private final Gender gender;
    private final LocalDate birthDate;
    private final Double weight;
    private final Double height;
}

public enum Gender {
    MALE("Mężczyzna"), FEMALE("Kobieta");
    private final String value;
    Gender(String value) {
        this.value = value;
    }
    public String getValue() {
        return value;
    }
}

To widziałem w taki sposób połączone, tworząc 2 klasy dla użytkownika i jego danych personalnych chciałem odseparować czysto technicznego użytkownika od tego bardziej biznesowego.

tym bardziej, że inne rzeczy typu UserRepository,UserReader,UserConfiguration` (kontroler pomine, bo jest w spoko miejscu) sa porozrzucane każdy gdzie indziej.

Tutaj cały koncept rozumiem tak
-UserReader - to jest mój useCase, klasa będzie odpowiedzialna za zwracanie danych użytkownika w różny sposób.
-UserRepository - port mojej domeny, czyli szczegół z jej punktu widzenia który powinienem zaimplementować poza nią, w tym przypadku mam InMemory, ale prosto mogę wpiąć tutaj Mongo albo Postgresa.
-UserConfiguration - warstwa konfiguracji z tego względu, że tutaj realizuje DI dla useCaseów mojej domeny, czyli wstrzykuje odpowiednie repository póki co.

Dlatego jest to wszystko trochę porozrzucane.

Włóż je w jedno miejsce, stwórz jakieś publiczne API, cała reszta ma byc niedostępna poza API, domenę zacznij wpychać jak najwięcej do obiektów, jak najmniej do serwisów i bedzie ok.
Podejść jest wiele, ja polecam package by component, czyli każdy moduł ładujesz do osobnego pakietu.

Nawiązujesz do czegoś takiego?
Powiedzmy że zamykam wszystko w package scope, publiczna zostaje fasada, dtosy no i interfejsy, których metody zwracają obiekt z domeny, obiekt który jest package scoped. Jak to zaimplementować poza tym pakietem?

Kompletnie olej Springa. Jednym z założeń Clean Architecture jest to, by kod domenowy (biznesowy) był odseparowany od warstwy aplikacji i infrastruktury, żeby był całkowicie odporny na zmiany w nich.

Jednym z założeń tego side projektu było własnie poćwiczenie zaimplementowania "czystego" połączenia między logika biznesową a frameworkiem, do tego to co chcę zrealizować praktycznie od początku wymaga zalogowanego użytkownika, tak więc wrzuciłem już prostą konfigurację Security opartą o JWT.

Niech te obiekty zaczną mieć jakieś zachowania, zamiast pokazywać jakie mają pola.

A o tym nie myślałem, mam widocznie mocno zakorzenione stare podejście architektury warstwowej i entity, nawet jeżeli jest to encja biznesowa traktuje jak worek na dane, a wszelką logikę planowałem robić w useCaseach.

hcubyc
  • Rejestracja:ponad 12 lat
  • Ostatnio:prawie 3 lata
3

Nawiązujesz do czegoś takiego?

Tak. Jak pisałem, to jedno z podejść do CA, generalnie polecam ksiazke uncle boba - Clean Architecture, tam będzie to opisane.

Powiedzmy że zamykam wszystko w package scope, publiczna zostaje fasada, dtosy no i interfejsy, których metody zwracają obiekt z domeny, obiekt który jest package scoped. Jak to zaimplementować poza tym pakietem?

Interfejsy i fasada zwracają DTOsy, które muszą byc publiczne, żeby dalo się z tego korzystać i implementować - więc fasada, przyjmuje jakiegoś DTOsy - typowe struktury danych, wewnatrz operuje na obiektach domenowych i na wyjściu znów mapuje na DTOsy, exceptiony też będa API domeny jeżeli bedziesz je rzucać.

Jednym z założeń tego side projektu było własnie poćwiczenie zaimplementowania "czystego" połączenia między logika biznesową a frameworkiem, do tego to co chcę zrealizować praktycznie od początku wymaga zalogowanego użytkownika, tak więc wrzuciłem już prostą konfigurację Security opartą o JWT.

ale jednym z założeń CA jest to, żeby to infrastruktura zależala od domeny, a nie odwrót i żeby domena była maksymalnie abstrakcyjna i żadna cześc infrastruktry nie wpadła do domeny (czy to jako klasa czy nawet abstrakcja). Stawia to pewne wyzwania przed developerem, bo faktycznie domena może potrzebować użytkownika z jakimiś rolami czy coś w ten deseń, wtedy musisz nałożyć na to abstrakcję, a potem po prostu zaimplementować, może być przez JWT. Generalnie uważam, że robienie tego dobrze wymaga zdecydowanie więcej niż jednego projektu i wielu prób i błędów - musisz próbować wielu podejść, np. walidacja w encji, walidacja w obiektach, to jak zmapujesz sobie 'security' na koncept domenowy i wiele innych. Aczkolwiek moim zdaniem zdecydowanie warto, bo za każdą 'iteracją' będziesz miał wrażenie, że robisz to co raz lepiej. Jedno z podejść jakie widziałem to np. customowe adnotacje w domenie. Np chcesz dodać metryki w kodzie, żeby np mierzyć ile czasu zajmuje domenie (i wszystkim jej zależnościa) przetworzenie requestu użytkownika, można to zrobić tak, że po prostu wystawiasz interfejs gdzies poza domeną, który udostepnia API metryk, a można np. zrobić customową adnotację, która potem przez np AOP bedzie mierzyła wykonani danej metody. Różnie to ludzie interpretują.


Limitations are limitless > ##### Ola Nordmann napisał(a)
> Moim językiem ojczystym jest C++ i proszę uszanować to, że piszę po polsku.
AP
  • Rejestracja:około 6 lat
  • Ostatnio:3 miesiące
  • Postów:164
1

Wygląda to teraz tak, InMemoryUserRepository póki co operuje na dto zamiast encji bazodanowej.
screenshot-20190804130656.png
Wewnątrz domeny jest jedna klasa która łączy ją ze Springiem, tego się nie dało uniknąć

Kopiuj
@Configuration
class UserConfiguration {
    private final UserRepository userRepository = new InMemoryUserRepository();
    private final VerificationTokenRepository verificationTokenRepository = new InMemoryVerificationTokenRepository();

    @Bean
    UserFacade userFacade() {
        ReadUserUseCase readUser = new ReadUserUseCase(userRepository);
        RegisterUserUseCase registerUser = new RegisterUserUseCase(userRepository, verificationTokenRepository);
        return new UserFacade(readUser, registerUser);
    }
}

Dtosy, interfejsy, fasada są publiczne i to nimi komunikuje się z innymi warstwami aplikacji, klasy encji biznesowych oraz useCaseów są package scoped. Wymusza to jednak zwracanie dtosów z tych interfejsów

Kopiuj
public interface UserRepository {
    Optional<UserDto> findUser(String username);
    void save(UserDto user);
}
public interface VerificationTokenRepository {
    UUID generateVerificationToken(String username);
}

Co wymaga dodatkowego mapowania

Kopiuj
  Optional<PersonalDetailsDto> readPersonalData(String username) {
        return repository.findUser(username)
                .map(User::fromDto)
                .map(User::getPersonalDetails)
                .map(PersonalDetails::toDto);
    }

No i tutaj widzę minus takiego rozwiązania, wymaga dużo mapowań :/
SQL/NoSQL -> DatabaseEntity -> DTO -> DomainEntity -> DTO -> Web

Package scope wewnątrz domeny dodatkowo nie pozwala na dodatkowe rozbicie tego na mniejsze pakiety, a prosta domena będzie się rozrastać i to sporo.

Co do obiektów domenowych, rozbiłem to w taki sposób

Kopiuj
@RequiredArgsConstructor
class RegisterUserUseCase {
    private final UserRepository userRepository;
    private final VerificationTokenRepository verificationTokenRepository;

    Either<UserError, UUID> register(RegisterUserDto registerUserDto) {
        if(userRepository.findUser(registerUserDto.getUsername()).isPresent())
            return Either.left(UserError.USERNAME_ALREADY_EXISTS);
        var userCreationResult = User.createUser(registerUserDto);
        return userCreationResult
            .map(user -> {
                userRepository.save(user.toDto());
                return verificationTokenRepository.generateVerificationToken(user.getUsername());
            });
    }
}
Kopiuj
static Either<UserError, User> createUser(RegisterUserDto dto) {
        return validEmail(dto.getEmail()) ?
            Either.right(User.builder()
                    .username(dto.getUsername())
                    .password(dto.getPassword())
                    .email(dto.getEmail())
                    .active(false)
                    .roles(new String[] {"USER"})
                    .build()) :
            Either.left(UserError.INVALID_EMAIL);
    }

Docelowo oczywiście będzie więcej logiki po jednej i drugiej stronie, ale chodzi o ogólny szkic.
Email weryfikuje za pomocą apache-commons, nie pisałem tego z palca - chyba nie jest zabronione coś takiego wewnątrz obiektu domenowego? ;)
Wszelkie sugestie co zepsułem mile widziane ;)

edytowany 1x, ostatnio: AngryProgrammer
danek
  • Rejestracja:ponad 10 lat
  • Ostatnio:7 miesięcy
  • Lokalizacja:Poznań
  • Postów:797
1
AngryProgrammer napisał(a):

No i tutaj widzę minus takiego rozwiązania, wymaga dużo mapowań :/
SQL/NoSQL -> DatabaseEntity -> DTO -> DomainEntity -> DTO -> Web

Jeśli robisz coś co jest CRUDem to niestety tak będzie. Tego typu architektura pokazuje swoje zalety dopiero przy jakichś bardziej złożonych projektach.
Btw, dobrze, że używasz Either ;)

Zamiast sprawdzać Optional.isPresent, możesz użyć Option z vavra który ma metode toEither która robi to co robisz tam tym ifem ;)


Spring? Ja tam wole mieć kontrole nad kodem ᕙ(ꔢ)ᕗ
Haste - mała biblioteka do testów z czasem.
edytowany 1x, ostatnio: danek
hcubyc
  • Rejestracja:ponad 12 lat
  • Ostatnio:prawie 3 lata
1

Wewnątrz domeny jest jedna klasa która łączy ją ze Springiem, tego się nie dało uniknąć

nic złego

Wymusza to jednak zwracanie dtosów z tych interfejsów

tak ma być, domena komunikuje się przez API (fasada, interfejsy i DTOsy) ze swiatem zewnetrznym, a wewnatrz korzysta z niedostepnych publicznie obiektow domenowych

Email weryfikuje za pomocą apache-commons, nie pisałem tego z palca - chyba nie jest zabronione coś takiego wewnątrz obiektu domenowego?

nie jest zabronione, możesz tak robić.

Package scope wewnątrz domeny dodatkowo nie pozwala na dodatkowe rozbicie tego na mniejsze pakiety, a prosta domena będzie się rozrastać i to sporo.

Zazwyczaj jak się bardzo rozrośnie to czasem może być tak, że moduł sam w sobie jest za duży i trzeba go rozbić na mniejsze.

Wszelkie sugestie co zepsułem mile widziane

VerificationTokenRepository to IMHO zły poziom abstrakcji. Jaki token? Potrzebujesz dane o zalogowanym użytkowniku, token to sposób w jaki to realizujesz w implementacji, więc poziom abstrakcji nie ten
a no i jeżeli na rysunku jest zaznaczony Use Case to nie znaczy, że klasy muszą się tak nazywać, ale to jak wolisz ;)


Limitations are limitless > ##### Ola Nordmann napisał(a)
> Moim językiem ojczystym jest C++ i proszę uszanować to, że piszę po polsku.
edytowany 1x, ostatnio: hcubyc
AP
Co do VerificationTokenRepository, może nie zbyt trafna nazwa - jest to token używany do aktywacji konta po rejestracji, po zalogowaniu user jest nieaktywny, odkładam token i username na bazie i wystawiam endpoint /user/confirmRegistration?token= na którym można aktywować konto połączone z tym tokenem.
hcubyc
dlaczego ten token nie może być trzymamy po prostu w UserRepository? albo jakaś nazwa, która ukryje że chodzi o token, bo to szczegół implementacji, generalnie chodzi o weryfikacje użytkownika
AP
Fakt, nie potrzebuje do tego osobnego repo, wrzucę to do userRepository. Poza tym wydaje się że mam co potrzebuje, zostaje trzymać się tych zasad i zobaczę co z tego wyjdzie. Dzięki za wskazówki.
Koziołek
Moderator
  • Rejestracja:około 18 lat
  • Ostatnio:22 dni
  • Lokalizacja:Stacktrace
  • Postów:6821
1

Wewnątrz domeny jest jedna klasa która łączy ją ze Springiem, tego się nie dało uniknąć

nic złego

I na to też jest rozwiązanie – mapowianie w XMLu lub za pomocą configuration beans, a nie za pomocą adnotacji w samych klasach.

@AngryProgrammer jest kilka małych rzeczy, które cię trafią później np. używanie tłumaczeń w enumach. Czy parametr w MALE("mężczyzna") coś wnosi? Wywal do prezentera i niech on się martwi jak zinterpretować Gender.MALE. Poza tym

-UserRepository - port mojej domeny, czyli szczegół z jej punktu widzenia który powinienem zaimplementować poza nią, w tym przypadku mam InMemory, ale prosto mogę wpiąć tutaj Mongo albo Postgresa.

Port nie służy do tego. Port opisuje, jak można czegoś użyć. Ładnie to ktoś kiedyś ujął, że „port is for …ing something”. To, co chcesz tu osiągnąć, to… heksagon, czyli jakaś domena (zapis do bazy). Prawidłowo sklejone elementy powinny wyglądać mniej więcej tak:

UserSpace – interfejs udostępniający operacje – polecenia na użytkownikach
UserReader – interfejs udostępniający operacje – zapytania na użytkownikach
UserRepositoryConnector – adapter modułu zapisu do bazy danych (jakkolwiek rozumianej), który jest zależnością pakietu domain.user do technical.storage.user.

I w środku cała reszta, już niewidoczna poza pakietem.


Sięgam tam, gdzie wzrok nie sięga… a tam NullPointerException
TE
  • Rejestracja:ponad 11 lat
  • Ostatnio:3 dni
  • Postów:67
0

A ja mam pytanie do podejścia Kuby Nabrdalika.
Stosuje je w pracy i namawiam kolegów z zespołu żeby też to robili (chociaż cięzko to idzie :( ). Ostatnio pojawił się argument jak stosować transakcyjność w tej fasadzie? Przy użyciu klasy konfigurującej (używając @Configuration i @Bean) nie mogę użyć adnotacji @Transactional :( Najlepszym (jedynym?) rozwiązaniem jest użycie tego o czym mowił @jarekr000000

ale to już jest za dużo "nie-springowych udziwnień" dla kolegów. Nie widzę innego rozwiązania tutaj. Macie jakieś pomysły?

danek
  • Rejestracja:ponad 10 lat
  • Ostatnio:7 miesięcy
  • Lokalizacja:Poznań
  • Postów:797
0

Albo tak jak Jarek mówi, albo robisz jakiś własny (prosty) mechanizm transakcji na warstwie dostępu do bazy danych


Spring? Ja tam wole mieć kontrole nad kodem ᕙ(ꔢ)ᕗ
Haste - mała biblioteka do testów z czasem.
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:40 minut
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4708
0

Nie ma clean architecture w Springu.
Jak coś wewnątrz cebuli zależy od wstrzyknięć springowych to niejako kładzie to ideę.
Ale można Spring z clean architecture pogodzić. Tylko wtedy jest to clean architecture pomimo Springa.


jeden i pół terabajta powinno wystarczyć każdemu
Zobacz pozostały 1 komentarz
jarekr000000
Da się. I nawet miałem takie projekty. I nie wszystko było złe. Przynajmniej testy (i to z perzystencją) były szybkie(bo nie odpalały kontenera). Ale nie miałem springdata.
danek
" Ale nie miałem springdata." - to wada czy zaleta? :P
jarekr000000
Zależy. Magia springdata wygląda tragiczno-komicznie. Ale w praktyce nie jest tak niebezpieczna jak samo JPA/Hibernate. I dodatkowo to faktycznie jeden z niewielu elementów springa, który coś faktycznie usprawnia (jeśli korzystasz z JPA) i zmniejsza ilość pisanego kodu.
jarekr000000
Widziałem, ale nie używałem. Potencjalnie dużo mniej niebezpieczne.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.