Framework do pisania CRUDów polecacie?

2

Cześć, słyszałem ze niektóre firmy przepisują CRUD z Javy do Rusta i zastanawiam się czy jest jakiś dobry framework lub biblioteka do tego. Przy czym bardziej chodzi mi o rustowy odpowiednik Akka-HTTP i JOOQ/Slice niż spasioną kobyłę jak Spring

0

@KamilAdam:

Serio przepisują? Moze dostałeś tylko pół informacji, np tylko 5% krytyczne wydajnościowo?
Nie wyobrażam sobie płynnego rozwoju rozległego, uniwersalnego systemu w języku bez refleksji, toż to zrobienie i UŻYTKOWANIE jakiejkolwiek biblioteki, to straszliwe schody
Podobnie jak fw w Delphi, C, C++. Niby można, ale nikt tego nie robi (oprócz w/w 5%). I raczej jednowarstwowe aplikacje na konkretny projekt

2

@AnyKtokolwiek: a po co ci ta refleksja? Np w Slick refleksja jest bardzo słabo używana. Slick nie używa refleksji przy mapowaniu z i do emocji bazodanowych. Podobnie przy serializacji do JSONa nie używa się w Scali refleksji. Wiec na co ta refleksja w zasadzie jest potrzebna?

4
AnyKtokolwiek napisał(a):

Nie wyobrażam sobie płynnego rozwoju rozległego, uniwersalnego systemu w języku bez refleksji, toż to zrobienie i UŻYTKOWANIE jakiejkolwiek biblioteki, to straszliwe schody

Zgadzam się z tym, że napisanie jakiejkolwiek dużej biblioteki (a tym bardziej frameworka) to dużo roboty, ale nie zgadzam się z tym, że nie da się tego zrobić bez refleksji. W najgorszym wypadku wpadnie trochę więcej boilerplate'u w niektórych miejscach.

0
wartek01 napisał(a):
AnyKtokolwiek napisał(a):

Nie wyobrażam sobie płynnego rozwoju rozległego, uniwersalnego systemu w języku bez refleksji, toż to zrobienie i UŻYTKOWANIE jakiejkolwiek biblioteki, to straszliwe schody

Zgadzam się z tym, że napisanie jakiejkolwiek dużej biblioteki (a tym bardziej frameworka) to dużo roboty, ale nie zgadzam się z tym, że nie da się tego zrobić bez refleksji. W najgorszym wypadku wpadnie trochę więcej boilerplate'u w niektórych miejscach.

Dokładnie, da się zastąpić znaczną ilością boilerplate'u. Np w C++ się jedzie makrami z ##

Różnica układu opartego na refleksji a bez niej jest taka, że albo np do silnika raportów lub generatora formatek CRUD możemy podać jakikolwiek obiekt (który sam się dokumentuje) a musi być przygotowany (implementować interfejsy, czy w inny sposób).

Jak ktoś pamięta EJB 1/2 (ja nie, zerkałem "przez ramię" z trzeciego rzędu) to tam był przymus implementowania interfejsów, coby nie powiedzieć POJO to krok we właściwą stronę.

2

Aż mnie zaciekawiłeś - co takiego można łatwo osiągnąć refleksją, czego nie da się łatwo osiągnąć procmakrami oraz traitami? Powiedziałbym nawet, że podejście Rusta jest dużo lepsze, ponieważ makra działają w czasie kompilacji :-)

0
AnyKtokolwiek napisał(a):

Różnica układu opartego na refleksji a bez niej jest taka, że albo np do silnika raportów lub generatora formatek CRUD możemy podać jakikolwiek obiekt (który sam się dokumentuje) a musi być przygotowany (implementować interfejsy, czy w inny sposób).

Nie do końca rozumiem o co ci chodzi ale w Ruscie dla dowolnego istniejącego już typu struktury (odpowiedniku klasy) możesz dalej implementować trakty (odpowiedniki interfejsów). Podobnie jest w Swifcie tylko tam są protokoły i Haskellu tylko tam są klasy typów.

0

@KamilAdam: @Patryk27:

Ale oczywiście, że się da interfejsować *), tylko pytanie, czy należy (przeróbka Bartoszewskiego)

Jak ja mam Customera, to podaję go takiego samego do frontu (przyjmijmy swój), do silnika raportowego (przyjmijmy że to obcy produkt), a może i do perzystencji (wieloletnia libka firmowa)

Wy chcecie intefejsowac go dwa (lub więcej) razy.
W moim odczuciu rynek już dawno wybrał, że POJO (w ekosystemie Javy) jest lepsze od interfejsowanych klas. To samo POJO mogę podać do siedemnastu bibliotek/frameworków, których autorzy nigdy niczego ze sobą nie uzgodnili.

nawet czuję, co odpowiecie: CustomerForPrint, CustomerForFront, CustomerMiddleLayer, CustomerEntity
Może w korpo tak się pisze, ja mam wątpliwości czy to rozsądne.

ps. Mocno (względnie) przezywam syf w C++, gdzie każdy framework ma swojego stringa, to taka analogia

*) naście lat temu brałem się za to w C++, na szczęście zaraziłem się Javą i (wtedy w Swingu) wszystko stało się jaśniejsze

1

@AnyKtokolwiek: no spoko - rynek "wybrał", że POJO est lepsze od "interfejsowanych klas". Tyle tylko, że - jak sam piszesz:

nawet czuję, co odpowiecie: CustomerForPrint, CustomerForFront, CustomerMiddleLayer, CustomerEntity
Może w korpo tak się pisze, ja mam wątpliwości czy to rozsądne.

Każde podejście ma swoje wady i zalety. Wadą jednolitego POJO, które jest konwertowane przez wszystkie mechanizmy jest to, że łatwo coś zepsuć o tym nie wiedząc. Wadą takich oddzielnych klas jest to, że trzeba trochę więcej kodu napisać.
Przy mniejszych i średnich projektach podejście pierwsze może się sprawdzać. Przy większych - podejście drugie jest raczej lepsze.

1

@AnyKtokolwiek: w Ruście miałbyś przecież jedną strukturę i ewentualnie kilka #[derive(...)], w stylu:

#[derive(DatabaseEntity, ReportEntity, Whatever)]
struct Customer {
   ...
}

... no copy-paste needed here; chyba że czegoś nie dostrzegam :-)

0
wartek01 napisał(a):

@AnyKtokolwiek: no spoko - rynek "wybrał", że POJO est lepsze od "interfejsowanych klas". Tyle tylko, że - jak sam piszesz:

nawet czuję, co odpowiecie: CustomerForPrint, CustomerForFront, CustomerMiddleLayer, CustomerEntity
Może w korpo tak się pisze, ja mam wątpliwości czy to rozsądne.

Wadą takich oddzielnych klas jest to, że trzeba trochę więcej kodu napisać.

Napisać to pikuś, ale to tzreba konserwować. Za dużo widziałem sklepów internetowych, gdzie coś się pozytywnie waliduje na froncie, ale leci wyjątek z backendu.
Z mojej perspektywy o wiele bezpieczniej się czuję bez niepotzrebnego mnożenia DTO

3

@AnyKtokolwiek: jeśli chodzi o to jak się implementuje własne derive to trzeba napisać makro. Co do samego ReportEntity to zamiast ReportEntity::get_fields oraz ReportEntity::get_value możesz zamiast tego mieć wizytora, przez co generowany jest kod w postaci:

struct Customer {
  name: String,
  age: usize,
}

trait ReportEntity for Customer {
  fn report(&self, reporter: &mut Reporter) -> io::Result<()> {
    reporter.add_str("name", self.name)?;
    reporter.add_usize("age", self.age)?;

    Ok(())
  }
}

Dzięki czemu nie musisz mieć refleksji w runtime, a jedynie w compile-time.

0

@hauleth:

Kapuję. Jeszcze getery po nazwie by trzeba.

Na pewno to wygląda ładniej, czytelniej, niż w templejtkach C++ bindingi miedzy kodem natywnym (struktury, ale też klasy z metodami) a interpreterem, gdzie trzeba wystawić nazwane stringiem (luabind, boost::python, których kiedyś używałem).

Istota zagadnienia bardzo podobna.

1

@AnyKtokolwiek: a po co? Zrób pola publiczne jak potrzebujesz je czytać poza modułem, w którym są zdefiniowane.

7

Jeszcze getery po nazwie by trzeba.

I od razu wiesz ze rozmawiasz z Javovcem... :D :D

1

Wracając do tematu: actix-web jest całkiem spoko, o ile go sparujesz z czymś mocnym do ui na froncie (przy czym tu nie podpowiem, bo nie moja bajka).

0
stivens napisał(a):
AnyKtokolwiek

Jeszcze getery po nazwie by trzeba.

I od razu wiesz ze rozmawiasz z Javovcem... :D :D

nie rozumiem o co pytasz. — @hauleth

Dlaczego?
Może raczej z plusowcem. Javowiec w sposób oczywisty by jechał refleksją

Nie mówcie, że w w/w Rust trait jest po cichu zawarte pobieranie w jednym module, z modułu który był nieznany na etapie kompilacji - bo nie wierzę.

class CustomerWrapper {
       private:
          const Customer * parent;

public:
  // C++ , przynajmniej starsze wersje, ma problem z typem zwracanym
        AnyCokolwiek get(string field_name);
}


Bez takiego pośrednictwa silnik raportów / webowy (nigdy nie dotykałem w C++) lub interpreter (dotykałem, i to niemało: Python, Lua, własny silnik C++/język do wydruków) nie zintegruje się z częścią binarną

4

Tylko Javovcy wciskaja na sile get i set bez logiki.

Jak masz setter, ktory robi { this.foo = foo } to rownie dobrze mozesz wywalic enkapsulacje. A jak nie masz settera w ogole to wystarczy publiczne, niemutowalne pole.

1

@AnyKtokolwiek: ale po co Ci to pobieranie? Dałem przykład jak można zaimplementować taki reporting w taki sposób, że dynamicznie możesz użyć takiego komponentu do budowania raportów.

Mając ww. przykład można stworzyć:

#[derive(Default)]
struct JSONReporter {
  // …
}

impl Reporter for JSONReporter {
  fn add_str(&mut self, key: &str, value: &str) {
    self.output(key, value);
  }

  fn add_usize(&mut self, key: &str, value: size) {
    self.output(key, value.to_string())
  }

  // …
}

fn gen_json_report(data: &dyn ReportEntity) {
  let mut reporter = JSONReporter::default();

  data.report(&mut reporter);
}

W ten sposób możesz przekazać jako argument do gen_json_report cokolwiek, co implementuje trait ReportEntity i dynamicznie, w czasie wykonania, będzie wybrana odpowiednia implementacja poprzez vtable. Na co Ci jakiekolwiek gettery czy settery?

0

@hauleth:

Rust ma konsekwentnego "przodka" wszystkich obiektów, z prymitywami włącznie ?

0

@AnyKtokolwiek: Rust nie jest językiem obiektowym. Nie ma obiektów. Nie ma dziedziczenia pól. Nie ma dziedziczenia implementacji za wyjątkiem domyślnej implementacji zawartej w traitach
UPDATE powiedz proszę jaki problem chcesz tym przodkiem rozwiązać bo rozwiązania z Javy nie mapują się na Rusta 1 do 1

6

Po cholerę pisać w Rust jakieś CRUDy.
Masakra jakaś. Nie każdy język musi służyć do pisania weba.
Już Golanga dostatecznie popsuto.

No i po co jak w czymś innym zrobicie to szybciej...

Pozniej każdy jeden język jest do wszystkiego i do niczego.

9
karsa napisał(a):

Po cholerę pisać w Rust jakieś CRUDy.

Ludzie się tak zafiksowują na wydajności i low-latency, że zapominają o innych przewagach Rust nad większością popularnych języków. Porównajmy z Java:

  • Inferencja typów
  • Makra > refleksja runtime, zdecydowanie mniej magii
  • Prostszy i bardziej spójny system obsługi błędów, który się nie gryzie z FP
  • traity są bardziej ekspresywne od klas/interfejsów, lepiej działają z genericsami
  • Package private, które działa jak trzeba
  • Brak wymazywania typów, można zrobić T::new, gdzie T to typ generyczny, można zaimplementować ten sam interfejs kilka razy dla różnych typów generycznych itp
  • Sprawdzanie w czasie kompilacji rzeczy które Java umie sprawdzać tylko w runtime (np. użycie zasobu po zamknięciu)
  • Sprawdzanie w czasie kompilacji rzeczy, których Java w ogóle nie umie sprawdzać nawet w runtime (np. wyścigi w danych)
  • Świetny system paczkowania cargo
  • Wbudowane cargo fmt / check / clippy / audit
  • Sum types
  • Pattern matching
  • Typy unsigned
  • Natywne operacje na liczbach 128-bitowych bez tego całego BigIntegerowego nonsensu
  • Świetne komunikaty o błedach, bardzo przyjazny kompilator
  • Deterministycznie sprzątanie zasobów (try w Javie to zabawka)
2

Best framework is no framework.

0
Riddle napisał(a):

Best framework is no framework.

:D

To jaką biblitekę do pisania serwera HTTP/REST polecacie? Coś lekkiego jak akka-http szukam
Jaką bibliotekę do obsługi SQLa polecacie? Najlepiej z typowanym SQLem jak Slick, ewentualnie jak JOOQ
:P

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

Best framework is no framework.

:D

To jaką biblitekę do pisania serwera HTTP/REST polecacie? Coś lekkiego jak akka-http szukam
Jaką bibliotekę do obsługi SQLa polecacie? Najlepiej z typowanym SQLem jak Slick, ewentualnie jak JOOQ
:P

Actix-web do backendu, jeżeli chodzi o SQL to Diesel.

1

Obecnie za wcześnie na takie połączenie, ale czekam niecierpliwie na rozwój sytuacji.

https://www.reddit.com/r/rust/comments/17b3ltu/my_journey_modifying_the_rust_compiler_to_target/
https://github.com/dwdkls/pizzamvc

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