Dlaczego warto stosować Clean Code (onion)

0

Jak do tej pory pisałem sobie Api w oparciu o kontrolery i serwisy w których implementowałem logikę biznesową. Jednak jako, że ostatnimi czasy zgłębiłem sobie temat Clean Code postanowiłem, że kolejne moje "dzieło" ;) stworzę w oparciu o tą idę i tak też się stało. Teraz pora na wnioski i niestety mam z nimi problem ponieważ na chwilę obecną nie widzę sensu aby stosować tego typu architekturę. Wiem jednak, że o programowaniu niewiele wiem i moje spostrzeżenia prawdopodobnie wynika to z mojego niezrozumienia tematu lub braku doświadczenia wiec pokornie postanowiłem zapytać innych dlaczego warto stosować to Celan Code?
Moje wnioski póki co są takie, że:

  • więcej z tym roboty
  • trudniej znaleźć coś w drzewie projektu. W przypadku zwykłego rozwiązania wszystko mam posegregowane w folderach co jest czytelne jak dla mnie
  • czy projekt mam podzielony na warstwy czy nie, to i tak w razie jak bym musiał np. zmienić dostawcę bazy danych to i tak musiał będę coś pozmieniać w kodzie więc po co bawić się w Clean Code
5

Clean code, clean architecture, czy o co Ci chodzi?
Z jakich materiałów korzystałeś, i jak wygląda Twój projekt?

2

Clean Code != Clean Architecture

Chodzi ci o Clean/Onion Architecture

Hi_i_By napisał(a):

Jak do tej pory pisałem sobie Api w oparciu o kontrolery i serwisy w których implementowałem logikę biznesową.

I dobrze, architektura encja-serwis ma sens przy nieskomplikowanych pod kątem złożoności logiki biznesowej aplikacjach.

Hi_i_By napisał(a):

dlaczego warto stosować to Celan Code Architecture?
więcej z tym roboty

Tak.

Hi_i_By napisał(a):

trudniej znaleźć coś w drzewie projektu. W przypadku zwykłego rozwiązania wszystko mam posegregowane w folderach co jest czytelne jak dla mnie

To jak masz ułożone drzewo projektu (Solution Explorer) zależy tylko od ciebie, spójrz tutaj klik Co tam nieczytelnego?
screenshot-20230930090956.png

Hi_i_By napisał(a):

czy projekt mam podzielony na warstwy czy nie, to i tak w razie jak bym musiał np. zmienić dostawcę bazy danych to i tak musiał będę coś pozmieniać w kodzie więc po co bawić się w Clean Code

Zwykle tak. Wszystko zależy ile i jakich abstrakcji użyjesz, da się zrobić tak że Domena czy Core aplikacji nie będzie wiedział o ORM-ie czy bazie danych, ale wymaga to więcej pracy niż zrobienie zwykłego mapowania anemicznych encji na tabelki w bazie danych.

Generalnie Clean Architecture nie stosuje się aby cokolwiek wymieniać w projekcie, tylko aby mieć porządek w sytuacji gdy mamy dużo skomplikowanej logiki biznesowej i nie chcemy skończyć w sytuacji że ta logika jest rozsmarowana po różnych warstwach, bo jest to trudne w utrzymaniu. To że dzięki Clean Architecture możesz sobie wymienić warstwę czy jakiś szczegół infrastruktury to tylko "skutek uboczny"

0

@somekind

Składa się z czterech warstw (domeny, aplikacji, infrastruktury i prezentacji). Korzystałem z różnych materiałów głównie YT oraz google i chat gpt

@markone_dev
U mnie drzewo wygląda tak
screenshot-20230930092157.png

screenshot-20230930092630.png

Widzę, że masz projekt podzielony między foldery. Kiedy jest lepiej stosować foldery, a kiedy biblioteki?
W przypadku podziału na foldery nie ma chyba możliwość aby dodać zależności między warstwami tak jak to można zrobić z bibliotekami? A właśnie te zależności sprawiły mi najwięcej problemów ponieważ:

  • do warstwy Domeny musiałem przenieść niektóre modele DTO aby interfejsy repozytoriów miały do nich dostęp. Teraz DTO ma podzielone między dwie warstwy co mnie trochę kłuję w oczy ;)
  • miałem problemy z FluentValidatorem. Musiałem go przenieść z warstwy Aplikacji do warstwy Infrastruktury aby miał bezpośredni dostęp do dbContextu inaczej nie działał. Problem nie do końca został przeze mnie zdiagnozowany ale wstępnie wynikał z asynchroniczności, a konkretniej z tego, że czas życia obiektu dbContextu kończył się szybciej niż operacje serwisu.
0

Generalnie Clean Architecture nie stosuje się aby cokolwiek wymieniać w projekcie, tylko aby mieć porządek w sytuacji gdy mamy dużo skomplikowanej logiki biznesowej i nie chcemy skończyć w sytuacji że ta logika jest rozsmarowana po różnych warstwach, bo jest to trudne w utrzymaniu. To że dzięki Clean Architecture możesz sobie wymienić warstwę czy jakiś szczegół infrastruktury to tylko "skutek uboczny"

Ładnie to ująłeś, ale dla mnie nie bardzo ma to sens. Pozwól, że przedstawię jak to widzę.

Załóżmy, że faktycznie na początku będę próbował wyabstrachować komponenty infrastruktury. Jeśli komponent jest używany w małej części projektu i jeśli robi drobne rzeczy to faktycznie łatwo można zakodować alternatywny komponent i podmienić w późniejszej fazie projektu.

Natomiast w sytuacji, gdy masz komponent, który jest używany w większej skali projektu, co również pociąga za sobą więcej wymaganej złożoności w implementacji komponentu, a to pociąga za sobą większa zależność od specyficznych cech konkretnej infrastruktury. Wówczas nawet jeśli masz taka opracowaną architektura to i tak droga do wymiany szeroko stosowanego komponentu może być złożona i droga, bo projekt uzależni się od czegoś co robi dobrą robotę dla Twojego projektu/biznesu.

I tu powstaje dla mnie sprzeczność, bo skoro ta architektura jest przeznaczona pod większe projekty to rezultacie końcowym gryźć się z złożonością jaka istnieje w rozbudowanych projektach.

Ale załóżmy, że istnieje infrastruktura zbliżona, której chcielibyśmy użyć w miejsce poprzedniej infrastruktury. Oferuje podobne możliwości, ale niestety jest trochę inna (w tym sensie, że inaczej trzeba obsługiwać, i np. jest więcej interakcji, odpowiedzialności po stronie developera). Jest całkiem możliwe, że ta infrastruktura nie wstrzeli się we wcześniejszy interfejs komponentu. Natomiast próba zmiany interfejsu, na którym leży cała biznesowa część powoduje, że taki kod w rezultacie ciężko jest usunąć, bo większość projektu od tego zależy.

Co o tym myślisz?

0
Hi_i_By napisał(a):

Jak do tej pory pisałem sobie Api w oparciu o kontrolery i serwisy w których implementowałem logikę biznesową. Jednak jako, że ostatnimi czasy zgłębiłem sobie temat Clean Code postanowiłem, że kolejne moje "dzieło" ;) stworzę w oparciu o tą idę i tak też się stało. Teraz pora na wnioski i niestety mam z nimi problem ponieważ na chwilę obecną nie widzę sensu aby stosować tego typu architekturę.

Nie warto stosować nic na siłę.

Chociaż jeśli to projekt ćwiczeniowy, to warto próbować nowych rozwiązań.

Więc potrzebujesz zadecydować, co się bardziej opłaca.

  • stabilna architektura, którą już wypracowałeś (nie wiem jaka, ale piszesz, że obecne rozwiązanie ci się sprawdza). Tutaj kosztem będzie to, że się nie nauczysz nowej architektury.
  • spróbować nowego podejścia, co może mieć długofalowe korzyści (czegoś się nauczysz), jednak będzie cię kosztować czas oraz będzie nieść ze sobą ryzyko, że zrobisz jakieś przeinżynierowany cargo cult, który na dodatek nie będzie ci się sprawdzać do końca, więc będziesz mieszał go z kodem spaghetti (tak wygląda większość projektów, gdzie ktoś chciał zabłysnąć).

Jeszcze jest trzecie rozwiązanie, pośrednie i być może najlepsze:

  • rozkminić, o co tak naprawdę chodzi w tej nowej architekturze i nie próbować na siłę zmieniać wszystkiego, tylko zastanowić się, które elementy ci się faktycznie przydadzą. Zrobić coś inspirowanego nowym podejściem, a nie całkowicie z nim zgodne.
0

Usiadłem do tematu Clean Architecture tylko dlatego aby zrozumieć jakie benefity ten sposób kodzenia daje, niestety na tym etapie ich nie widzę. Może to wynika z mojego bardziej biznesowego niż programistycznego podejścia do tematu. Mianowicie istotą dla mnie jest jak najefektywniejsze wykorzystanie czasu na rozwiązanie danego problemu biznesowego gdzie kod to tylko narzędzie, a nie filozofia czy też sztuka. Zdaje sobie jednak sprawę, że w wyniku braku doświadczenia nie widzę zagrożeń do jakich to może doprowadzić, stąd pomysł aby zapytać o to innych.

1

Sam zwracasz uwagę na brak doświadczenia.

Tak jak koledzy już wcześniej wspominali, korzyści z reguły widać dopiero przy większym, bardziej skomplikowanym projekcie, a to wynika z prostej rzeczy - przy małych, prostych projektach faktycznie wstępny narzut jest wysoki w porównaniu do skomplikowania "biznesu".
W momencie jak poziom skomplikowania domeny oraz rozmiar projektu rośnie, ten wstępny narzut często się "gubi" wraz z upływem czasu.

Ale spokojnie, prawie na pewno prędzej czy później trafisz na spaghetti, nad którym nikt nie panował, gdzie po 15 latach rozwoju dodanie nowego pola do formularza zajmuje 3 tygodnie, nikt nie będzie wiedział czy to zadziała i nikt Ci nie będzie potrafił pomóc w rozwiązaniu problemu, bo zwyczajnie "nawet najstarsi Indianie tego nie wiedzą" :P

I takie właśnie doświadczenia często zmieniają perspektywę na sprawę - gdy zobaczysz, że to "biznesowe" podejście z efektywnym wykorzystaniem czasu w pewnym momencie staje się absolutnie nieefektywne :D

A w cwiczeniowych projektach... Czasami naprawdę trzeba się nakombinować, żeby podejście ukazało zalety.

Edyta:
Pytanie pomocnicze - czy piszesz testy automatyczne?
Jeśli nie, to zajrzyj teraz do poprzednich projektów i zastanów się czy jesteś w stanie w prosty i sensowny sposób wprowadzić tam choćby fragment piramidy testowej (choćby unit i Integration testy).

0

Korzystałem z różnych materiałów głównie YT oraz google i chat gpt

Ja bym się przyjrzał, o co tak naprawdę chodziło autorowi, a nazwę "clean architecture" wymyślił zdaje się Robert C. Martin aka Wujek Bob (chociaż jak się domyślam, opisał raczej to, co było już obecne w różnych projektach i wcześniej było znane pod innymi nazwami). Często jest tak, że autor coś wymyśli, a później cały internet źle/powierzchownie coś rozumie. Więc próbowałbym dojść do źródła i wyłapać intencje autora. Spróbować dociec dlaczego?, bo wiele osób skupia się tylko na pytaniu jak? i tylko naśladują, a nie wnikają w sens. Czyli kult cargo https://pl.wikipedia.org/wiki/Kulty_cargo

1
Hi_i_By napisał(a):

Widzę, że masz projekt podzielony między foldery. Kiedy jest lepiej stosować foldery, a kiedy biblioteki?

Biblioteki pozwalają na kontrolę nad zależnościami. Dzięki projektom możesz decydować który projekt ma zależności do którego. Przykładowo projekt Domain nie powinien mieć zależności do Infrastructure tylko w drugą stronę.

W przypadku podziału na foldery nie ma chyba możliwość aby dodać zależności między warstwami tak jak to można zrobić z bibliotekami? A właśnie te zależności sprawiły mi najwięcej problemów ponieważ:

  • do warstwy Domeny musiałem przenieść niektóre modele DTO aby interfejsy repozytoriów miały do nich dostęp. Teraz DTO ma podzielone między dwie warstwy co mnie trochę kłuję w oczy ;)

To nie wina architektury tylko twoja. DTO jak sama nazwa mówi to Data Transfer Object i jego miejsce jest w warstwie aplikacji która zwykle na wejściu przyjmuje DTO i w zależności od potrzeb mapuje na modele domenowe. W przypadku gdy nie używasz CQRS to tłumaczy modele domenowe na DTO. DTO jest kontraktem między Core aplikacji a zewnętrznym światem. W twoim przypadku warstwa persystencji operuje wyłącznie na encjach/modelu znajdującym się w Domain.

  • miałem problemy z FluentValidatorem. Musiałem go przenieść z warstwy Aplikacji do warstwy Infrastruktury aby miał bezpośredni dostęp do dbContextu inaczej nie działał.

Po raz kolejny twój błąd. Po to masz repozytorium aby w jakiś sposób ukryć DbContext. DbContext to warstwa infrastruktury (persystencji) i inne warstwy wyższe (Domain, Application) nie powinny wiedzieć czego używasz to persystencji stanu aplikacji. To co zrobiłeś to olałeś enkapsulację i pozwoliłeś żeby szczegół implementacyjny warstwy persystencji wyciekł do wyższych warstw.

Ja też używam FluentValidatora i nigdy nie miałem potrzeby żeby integrować go z warstwą persystencji. W moich aplikacjach FV siedzi sobie w warstwie aplikacji i waliduje sobie przychodzące z API DTO.

  • Problem nie do końca został przeze mnie zdiagnozowany ale wstępnie wynikał z asynchroniczności, a konkretniej z tego, że czas życia obiektu dbContextu kończył się szybciej niż operacje serwisu.

J/W odpowiednio zaimplementowana Clean/Onion Architecture sprawia że szczegóły implementacyjne warstw są odpowiednio ukryte.

1
znowutosamo6 napisał(a):

Natomiast w sytuacji, gdy masz komponent, który jest używany w większej skali projektu, co również pociąga za sobą więcej wymaganej złożoności w implementacji komponentu, a to pociąga za sobą większa zależność od specyficznych cech konkretnej infrastruktury. Wówczas nawet jeśli masz taka opracowaną architektura to i tak droga do wymiany szeroko stosowanego komponentu może być złożona i droga, bo projekt uzależni się od czegoś co robi dobrą robotę dla Twojego projektu/biznesu.

Tak. Decydując się na Onion/Clean Architecture nie skupiasz się na tym, żeby wymieniać warstwy, tylko żeby mieć dobrze zorganizowany projekt. To że jednym ze skutków ubocznych dobrze zaimplementowanej architektury jest opcja wymiany komponentu to tylko się cieszyć. Ale tak jak piszesz żeby to osiągnąć to trzeba poświęcić więcej czasu i nie zawsze jest to celem samym w sobie.

Weźmy sobie na przykład chęć wymiany ORM-a. Aby to zrobić bezboleśnie musisz w aplikacji mieć:

  • dwa modele Domenowy i Persystencji i robić mapowania pomiędzy nimi
  • interfejsy IRepositroy w Domenie a implementacje w Persistence.

Interfejs IRepository na wejściu i wyjściu przyjmuje/zwraca obiekty domenowe, a implementacja robi mapowanie pomiędzy modelami i zajmuje się utrwalaniem danych, np korzystając z DbContext w przypadku EF Core.
Serwisy aplikacyjne do persystencji i odczytu danych używają wyłączenie abstrakcji IRepository

Mając tak zaprojektowaną warstwę persystencji możesz w miarę bezboleśnie zmienić bazę danych czy ORM-a. Ale jak sam zauważyłeś wymaga to więcej pracy aby to zaprojektować i utrzymywać.

Dlatego wybierając architekturę trzeba się poważnie zastanowić co jest naszym celem. Czasem zwykła architektura encja-serwis będzie ok. Czasem (np w startupie) trzeba zacząć szybko aby dostarczyć produkt i przyciągnąć klientów. Jak już mamy ustabilizowany projekt, mamy klient i chcemy się skalować można zdecydować się na re-design. Poczytaj jak zaczynali najwięksi Facebook, Netflix, Amazon czy choćby Allegro. Pierwsze wersje które zarabiały pieniądze to były monolity z długiem technicznym, spaghetti i innymi problemami które nie pozwalaly na skalowanie i obsługę wzrastającej liczby klientów i nowych wymagań biznesowych. Dlatego decydowali się na stopniową migrację do architektur rozproszonych (mikroserwisy), chmury, itd.

1
Hi_i_By napisał(a):
  • trudniej znaleźć coś w drzewie projektu. W przypadku zwykłego rozwiązania wszystko mam posegregowane w folderach co jest czytelne jak dla mnie

Trzeba odróżnić architekturę od folderów i plików. Owszem, dobra architektura skutkuje zwykle też ładnym drzewkiem folderów i plików, ale to nie o to tutaj chodzi. Liczą się zależności i odpowiedzialność konkretnych modułów/klas itp.

Powiedzmy, że masz 2 projekty, które mają tak samo wyglądające drzewko plików - każdy plik tak samo nazwany. I to nic ci nie powie, bo jeden projekt może być napisany cudownie, gdzie każdy moduł ma swoją odpowiedzialność i nie jest sprzężony z innymi modułami bardziej niż to konieczne. A w drugim projekcie będziesz miał niejasne odpowiedzialności i dziwne zależności. Mimo że drzewko plików będzie takie samo.

Tak więc, jak już wizualizować architekturę, to jakiś diagram czy graf zależności miałby więcej sensu.

Być może twoim problemem nie jest w ogóle ta, czy inna architektura, tylko że zbytnio się skupiasz na rzeczach, które są efektem ubocznym (drzewko folderów), zamiast pomyśleć o tym jak o systemie powiązanych ze sobą rzeczy(modułów, klas itp.), które się nawzajem ze sobą komunikują.

1

Clean architecture i DDD daje bardzo dużo, bo Twoje rozwiązanie problemu biznesowego siedzi w warstwie domenowej i jest niezależne od infrastruktury. Każdy korzysta dziś na potęgę z RESTa, ale może przyjść dzień, w którym wyjdzie coś lepszego i takie rozwiązanie, często pisane latami dalej będziesz mógł wykorzystać bez przepisywania.

Dodatkowo, jakbym ja miał zaczynać startup to modularny monolit właśnie z DDD po stronie każdego modułu, bo później to łatwo przeniesc na mikroserwisy.

Clean/Onion są bardzo podobne do siebie i dają pewne metodyczne podejście do pracy przy projekcie, bo każdy mniej więcej wie o co chodzi, jeśli kojarzy tematy 😊

Ostatnio pisałem sobie coś na boku i providera do mailingu musiałem podmienić - jaka to przyjemność mając ładnie ogarnięta architekturę rozwiązania 😉

1

Jak zaczynasz dodawać coś takiego do takiej aplikacji jak np API to porywasz się z motyką na słońce.

Dużo lepiej jakbyś zaczął od dużo prostszej aplikacji, np jakiegoś toola konsolowego i tam spróbował dodać Clean Code/Clean architecture. Aplikacje webowe wbrew pozorom są bardzo skomplikowane, i nie jest łatwo od zera wprowadzić do nich taką drastyczną zmianę.

Spróbuj najpierw napisać jakiś tool CLI, i w nim oddziel logikę od UI. Napisz testy pod obie rzeczy i podziel się. To byłby dobry krok.

0

Mam kilka WebApi zrobionych "tradycyjnie" czyli w oparciu o kontrolery i serwisy odpowiedzialne za logikę. Obecnie już wystartowałem z nowym projektem w którym bazuje na o Clean Code/Clean Architecture. Niestety widzę, że nie jest to takie proste i wiele rzeczy robię na tzw. czuja. Jak to mówią pierwsze koty za płoty. Liczę, że potem będzie tylko lepiej aczkolwiek cały czas nie widzę po co stosować tą architekturę. Mam nadzieje, że w końcu załapię ;)

0
Hi_i_By napisał(a):

Mam kilka WebApi zrobionych "tradycyjnie" czyli w oparciu o kontrolery i serwisy odpowiedzialne za logikę. Obecnie już wystartowałem z nowym projektem w którym bazuje na o Clean Code/Clean Architecture. Niestety widzę, że nie jest to takie proste i wiele rzeczy robię na tzw. czuja. Jak to mówią pierwsze koty za płoty. Liczę, że potem będzie tylko lepiej aczkolwiek cały czas nie widzę po co stosować tą architekturę. Mam nadzieje, że w końcu załapię ;)

Chciałem powiedzieć że porywasz się z motyką na słońce, i prościej byłoby do tego podejść np z toolem albo programem CLI.

0
Riddle napisał(a):
Hi_i_By napisał(a):

Mam kilka WebApi zrobionych "tradycyjnie" czyli w oparciu o kontrolery i serwisy odpowiedzialne za logikę. Obecnie już wystartowałem z nowym projektem w którym bazuje na o Clean Code/Clean Architecture. Niestety widzę, że nie jest to takie proste i wiele rzeczy robię na tzw. czuja. Jak to mówią pierwsze koty za płoty. Liczę, że potem będzie tylko lepiej aczkolwiek cały czas nie widzę po co stosować tą architekturę. Mam nadzieje, że w końcu załapię ;)

Chciałem powiedzieć że porywasz się z motyką na słońce, i prościej byłoby do tego podejść np z toolem albo programem CLI.

Podpowiedz mi z jakimi problemami mogę się zderzyć? Wiesz zapewne tak jak mówisz na początku było by łatwiej zrobić coś konsolowego ale nie wiem też do końca co masz na myśli. Chodzi Ci o to, że przy aplikacji konsolowej łatwiej zrozumieć jak tworzy się CleanCod? Mi wydaje się, że to już rozgryzłem. Obecnie mam problem z interpretacją do jakiej warstwy upchnąć daną funkcjonalność. Wspomagam się google i chatemGPT i jakoś to pcham do przodu czekając na ścianę ;). Z drugiej strony jeśli coś pójdzie nie tak to pracuje nad swoim projektem, a nie komercyjnym za czyjeś pieniądze bo ja nawet nie jestem juniorem i zapewne najbliższym czasie ten stan rzeczy nie uległ zmianie w związku z obecną sytuacją w IT, ale mam wizję swojej apki i cisnę z nią dalej aby koniec końców ją uruchomić :)

0
Hi_i_By napisał(a):
Riddle napisał(a):
Hi_i_By napisał(a):

Mam kilka WebApi zrobionych "tradycyjnie" czyli w oparciu o kontrolery i serwisy odpowiedzialne za logikę. Obecnie już wystartowałem z nowym projektem w którym bazuje na o Clean Code/Clean Architecture. Niestety widzę, że nie jest to takie proste i wiele rzeczy robię na tzw. czuja. Jak to mówią pierwsze koty za płoty. Liczę, że potem będzie tylko lepiej aczkolwiek cały czas nie widzę po co stosować tą architekturę. Mam nadzieje, że w końcu załapię ;)

Chciałem powiedzieć że porywasz się z motyką na słońce, i prościej byłoby do tego podejść np z toolem albo programem CLI.

Podpowiedz mi z jakimi problemami mogę się zderzyć?

No moim zdaniem główny problem jaki znam, to to że w imię clean code'a ludzie wsadzają abstrakcje w nieodpowienie miejsca (tam gdzie nie powinni), ale za to omijają odpowiednie (nie wkłajadają tam gdzie powinni). Czyli innymi słowy - abstrakcje są wrzucane na opak.

Wiesz zapewne tak jak mówisz na początku było by łatwiej zrobić coś konsolowego ale nie wiem też do końca co masz na myśli. Chodzi Ci o to, że przy aplikacji konsolowej łatwiej zrozumieć jak tworzy się CleanCod?

Tak myślę, bo jest mniej zależności. W aplikacji webowej masz oprócz domeny: klient, static filesy, js, na pewno jakiś framework js, biblioteki klienta, .env, klucze, shareowanie kluczy, autoryzacja, http, rest, api, controllery, framework backendowy, bazę danych, orm'a, na pewno system budowania, dodatkowe biblioteki, masa rzeczy, które się przeplatają ze sobą. Wszystkie te rzeczy, idealnie powinny być od siebie poprawnie oddzielone w racjonalnych proporcjach, i nie jest to łtawe.

W aplikacji konsolowej masz tak na prawdę tylko domenę i interfejs stdio. Oddzielenie tego jest bardzo łatwe, i testowanie tego też jest łatwe.

Mi wydaje się, że to już rozgryzłem. Obecnie mam problem z interpretacją do jakiej warstwy upchnąć daną funkcjonalność.

No i to jest zupełnie odwrotne działanie. Najpierw powinieneś napisać jakiś kod, i potem spróbować zastanowić się jaką warstwę reprezentuje ten kod. Jak już zrozumiesz do jakiej warstwy należy kod, powinieneś w tym miejscu dodać odpowiednie abstrakcje. Oczywiście może być tak, że kod napisałeś jest pomieszany i należy do kilku warstw na raz. To jest wtedy trudniejsze zadanie bo wtedy najpierw trzeba "rozplątać" ten kod, na osobne warstwy, i dopiero potem je oddzielać. to miałem na myśli że jest to trudniejsze, jak masz dużo zależności, np w aplikacji webowej.

A nie, że myślisz "gdzie by tu wsadzić ten kod". To jest trochę odwrócone wnioskowanie.

Z drugiej strony jeśli coś pójdzie nie tak to pracuje nad swoim projektem, a nie komercyjnym za czyjeś pieniądze bo ja nawet nie jestem juniorem i zapewne najbliższym czasie ten stan rzeczy nie uległ zmianie w związku z obecną sytuacją w IT, ale mam wizję swojej apki i cisnę z nią dalej aby koniec końców ją uruchomić :)

Działanie aplikacji to jedno. To czy jest dobrze napisana to inne. Jest masa działających apek które są napisane jak shit.

Jest cytat od Roberta Martina, autora książki "Clean Code".

If the program works, but I don't understand it, as soon as the requirements change, that program is useless. If a given program doesn't work or even compile, but I can understand it and reason through its logic, I can fix any non-working program you throw at me.

To jest trochę rozszerzenie filozofii ownowania swojego kodu i nie bania się zmian.

Dla przykładu, znalazłem na stack overflow taką prostą gierkę w zgadywanie liczb: https://stackoverflow.com/questions/9086168/random-number-guessing-game

Niby kod na 30 linijek, ale jednak widać tu trzy poziomy abstrakcji:

  1. Sama gra
  2. Losowanie liczb pseudolosowych
  3. Interfejs konsolowy

Gdzie są poszczególne warstwy? Otóż tutaj:

screenshot-20231002150526.png

Dobre rozdzielenie tych odpowiedzialności nie jest proste.

PS: Tak, Main() nie należy do domeny gry tylko do interfejsu.

0

Może zacznij od hex arch? Onion to IMO sztuka dla sztuki, żeby było o czym robić artykuły i konferencje. Tak naprawdę ważne jest to, żeby kod domenowy był bez zewnętrznych zależności a one są dostępne jako implementację serwisów,

To jak rozmieścisz kod ma mało znaczenie w gruńcie rzeczy przeniesienie gotowego kodu do osobnych pakietów/modułów to prosta i mocno zautomatyzowana praca.

Przykładowo w go standarowym podejściem jest takie, że logika domenowa idzie do pakietu/modułu o nazwie /nazwa_domeny. W tym pakiecie jest logika i interfejsy. Implementacja jest w całkowicie niezależnym pakiecie (pomimo bycia pakietem w pakiecie to takie moduły nie mają ukrytych zależności między sobą): /nazwa_domeny/postgres /nazwa_domeny/nazwa_zewnętrznej_firmy_implementującej_HTTP_API, czyli nazwa mówiąca o tym dla kogo coś implementujemy. Samo łączenie zależności zazwyczaj robi się w mainie, które kolejno można rozbić na funkcje lub przenieść do osobnego pakietu jak nam coś urośnie, albo dana zależność jest używana przez wiele aplikacji (w przypadku monorepo i rozproszonego monolitu)

0
Riddle napisał(a):
Hi_i_By napisał(a):
Riddle napisał(a):
Hi_i_By napisał(a):

No moim zdaniem główny problem jaki znam, to to że w imię clean code'a ludzie wsadzają abstrakcje w nieodpowienie miejsca (tam gdzie nie powinni), ale za to omijają odpowiednie (nie wkłajadają tam gdzie powinni). Czyli innymi słowy - abstrakcje są wrzucane na opak.

Tak myślę, bo jest mniej zależności. W aplikacji webowej masz oprócz domeny: klient, static filesy, js, na pewno jakiś framework js, biblioteki klienta, .env, klucze, shareowanie kluczy, autoryzacja, http, rest, api, controllery, framework backendowy, bazę danych, orm'a, na pewno system budowania, dodatkowe biblioteki, masa rzeczy, które się przeplatają ze sobą. Wszystkie te rzeczy, idealnie powinny być od siebie poprawnie oddzielone w racjonalnych proporcjach, i nie jest to łtawe.

Tu trafiłeś w punkt

Możesz polecić jakieś źródło do zgłębiania tego tematu (pomijam książkę Clean Code bo ją dziś zamówię )

0
Hi_i_By napisał(a):

Tu trafiłeś w punkt

Możesz polecić jakieś źródło do zgłębiania tego tematu (pomijam książkę Clean Code bo ją dziś zamówię )

Nie zaczynaj od aplikacji webowych - to w zasadzie dwie aplikacje w jednym (klient i serwer), które z jednej strony powinny być rozłączne, żeby zmiany w jednej nie psuły durigje, ale z drugiej strony kompatybilne, bo z punktu widzenia użytkownika to jest jedna aplikacja.

Nawet pisanie aplikacji desktopowych nie jest z tego powodu łatwe, bo oddzielenie logiki od GUI jest dodatkowo skomplikowane przez eventy (onClick, onChange, etc.). Najprościej zacząć od aplikacji CLI.

Zobacz mój przykład wyżej z zaznaczonymi warstwami abstrakcji w screenie wyżej.

Moja rada to jest:

  1. Napisz banalny program CLI (jak np taką gre w zgadywanie, gre w wisielca) i spróboj go podzielić na warstwy
  2. Potem spróbuj anpisać apkę w WF, WPF
  3. Potem dopiero spróbuj przełożyć te zasady na aplikacje webowe.
0
Riddle napisał(a):
Hi_i_By napisał(a):

Tu trafiłeś w punkt

Możesz polecić jakieś źródło do zgłębiania tego tematu (pomijam książkę Clean Code bo ją dziś zamówię )

Nie zaczynaj od aplikacji webowych - to w zasadzie dwie aplikacje w jednym (klient i serwer), które z jednej strony powinny być rozłączne, żeby zmiany w jednej nie psuły durigje, ale z drugiej strony kompatybilne, bo z punktu widzenia użytkownika to jest jedna aplikacja.

Czy w przypadku WebApi nie ma wyraźnego oddzielenia klienta od serwera? To chyba są dwa niezależne twory? W przypadku MVC rzeczywiście coupling może być znaczący

0
Hi_i_By napisał(a):
Riddle napisał(a):
Hi_i_By napisał(a):

Tu trafiłeś w punkt

Możesz polecić jakieś źródło do zgłębiania tego tematu (pomijam książkę Clean Code bo ją dziś zamówię )

Nie zaczynaj od aplikacji webowych - to w zasadzie dwie aplikacje w jednym (klient i serwer), które z jednej strony powinny być rozłączne, żeby zmiany w jednej nie psuły durigje, ale z drugiej strony kompatybilne, bo z punktu widzenia użytkownika to jest jedna aplikacja.

Czy w przypadku WebApi nie ma wyraźnego oddzielenia klienta od serwera? To chyba są dwa niezależne twory? W przypadku MVC rzeczywiście coupling może być znaczący

No to jest właśnie pułapka. Koncepcyjnie niby tak. Ale w wielu aplikacjach, jak np zmienisz nazwę pola na serverze, to klient też się psuje i również wymaga zmiany. Z definicji, jeśli coś wymaga zmiany w dwóch miejscach, to nie jest na prawdę oddzielone.

To jest własnie paradoks, że niby WebApi ma służyć jako interfejs między klientem a serverem, ale tak na prawdę jest używane do dalszego couplingu tych dwóch aplikacji.

Żeby powiedzieć że klient jest oddzielony od servera, to powinno się dać móc zmienić nazwę, typ, albo strukturę pola na serverze, tak żeby klient nadal działał poprawnie i nie wymagał zmiany. Powinno się dać móc zmienić kodowanie, format, strefę czasowa, timestamp z sekund na milisekundy na serverze, i klient powinien działać tak jak działał. Jeśli po takiej zmianie klient się psuje, to nie był nigdy oddzielony. Dochodzi np do sytuacji w której klient odczytuje pole np created_at, które zostało stworzone tylko przez framework ORM na serverze. I odczytuje je w formacie ustalonym przez ORM. Całkowity brak warstw abstrakcji.

0
Riddle napisał(a):
Hi_i_By napisał(a):
Riddle napisał(a):
Hi_i_By napisał(a):

Tu trafiłeś w punkt

Możesz polecić jakieś źródło do zgłębiania tego tematu (pomijam książkę Clean Code bo ją dziś zamówię )

Nie zaczynaj od aplikacji webowych - to w zasadzie dwie aplikacje w jednym (klient i serwer), które z jednej strony powinny być rozłączne, żeby zmiany w jednej nie psuły durigje, ale z drugiej strony kompatybilne, bo z punktu widzenia użytkownika to jest jedna aplikacja.

Czy w przypadku WebApi nie ma wyraźnego oddzielenia klienta od serwera? To chyba są dwa niezależne twory? W przypadku MVC rzeczywiście coupling może być znaczący

No to jest właśnie pułapka. Koncepcyjnie niby tak. Ale w wielu aplikacjach, jak np zmienisz nazwę pola na serverze, to klient też się psuje i również wymaga zmiany. Z definicji, jeśli coś wymaga zmiany w dwóch miejscach, to nie jest na prawdę oddzielone.

To jest własnie paradoks, że niby WebApi ma służyć jako interfejs między klientem a serverem, ale tak na prawdę jest używane do dalszego couplingu tych dwóch aplikacji.

Żeby powiedzieć że klient jest oddzielony od servera, to powinno się dać móc zmienić nazwę, typ, albo strukturę pola na serverze, tak żeby klient nadal działał poprawnie i nie wymagał zmiany. Powinno się dać móc zmienić kodowanie, format, strefę czasowa, timestamp z sekund na milisekundy na serverze, i klient powinien działać tak jak działał. Jeśli po takiej zmianie klient się psuje, to nie był nigdy oddzielony. Dochodzi np do sytuacji w której klient odczytuje pole np created_at, które zostało stworzone tylko przez framework ORM na serverze. I odczytuje je w formacie ustalonym przez ORM. Całkowity brak warstw abstrakcji.

Rzeczywiście napisanie apki wg powyższego opisu to trudne zadanie.

<Z definicji, jeśli coś wymaga zmiany w dwóch miejscach, to nie jest na prawdę oddzielone.>
Dzięki za to zdanie. Zmienia perspektywę patrzenia na kod

2

<Z definicji, jeśli coś wymaga zmiany w dwóch miejscach, to nie jest na prawdę oddzielone.>

Dlatego istnieją DTO i wersjonowanie API. Jeżeli wystawiasz jakieś zasoby poprzez REST API to musisz zwracać przyjmować jakiś model w JSON czy XML, nie ważne. Jest model są pola, więc zawsze będzie występował jakiś coupling pomiędzy systemami i jak zmienisz nazwę/definicje jednego pola w DTO to klienty muszą się do tej zmiany dostosować.

W Clean/Onion Architecture chodzi o to, żeby wartwa kliencka czyli w tym przypadku API było niezależne od domeny i vice versa. Czyli zamieniam sobie w modelu boola na inta a klienty mojego API a nawet baza danych nie wie o tej zmianie.

0
markone_dev napisał(a):

<Z definicji, jeśli coś wymaga zmiany w dwóch miejscach, to nie jest na prawdę oddzielone.>

Dlatego istnieją DTO i wersjonowanie API.

To jest jeden ze sposobów zapewnienia oddzielenia tych dwóch aplikacji, nie jedyny z resztą.

0

To jest jeden ze sposobów zapewnienia oddzielenia tych dwóch aplikacji, nie jedyny z resztą.

Ale za to najpopularniejszy.

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