Czy reducer powinien zwracać Obserwable i Promisy?

Zastanawiam się, czy nie wywalić flagowej opcji z mojej biblioteki. A mianowicie fakt, że reducer może zwracać obserwable i opakowane w funkcję promisy.
Czyli teraz da się tak zrobić w Feedbacks:

function reducer(state, action) {
   return () => Promise.resolve(123);
}

albo:

function reducer(state, action) {
   return Rx.interval(1000);
}


Dlaczego chcę to wywalić?

No bo załóżmy, że moja biblioteka będzie używana w dużych projektach. Wtedy takie machinacje obserwablami czy zwracanie asynchronicznych funkcji bezpośrednio z reducerów mogłoby sprawić, że projekt stałby się trudniejszy w debugowaniu albo trudne byłoby robienie jakichś mocnych zmian w implementacji (ponieważ wszystko w środku byłoby sprzężone z tymi obserwablami).

Wpadłem na pomysł w jaki sposób mógłbym w bardziej elegancki sposób obsługiwać efekty uboczne.

Tzn. dalej reducer zwracałby efekt uboczny, tylko, że nie wprost obserwable/asynchroniczne funkcje, a raczej obiekt z parametrami efektu np.
{type: 'fetchData', params: {url: 'example.json'} }  

(przy czym zrobię helpery do robienia "effect creatorów", ale to już drobiazg).

takie podejście (że efekt byłby scharakteryzowany przez typ i parametry i dopiero potem zamieniony na "prawdziwy" efekt) sprawiłoby, że efekty mogłyby być np. serializowane (co będzie miało podobne zalety, jakie ma fakt, jeśli akcje w Reduxie są serializowalne - można to np. wysyłać przez sieć, zapamiętywać gdzieś w logach, odtwarzać później itp.).

Samo mięso (rozwiązywanie obsserwabli i promisów) byłoby zachowane, tzn. ten sam ogólny model działania miałaby moja biblioteka, z tą różnicą, że nie moglibyście zwrócić obserwabla czy funkcji zwracającej Promise bezpośrednio z Reducera, tylko że najpierw musielibyście zwrócić efekt, a potem ten efekt byłby zamieniony na Obserwabla czy Promise.

Od strony użytkowania biblioteki byłby to dodatkowy krok, albo dodatkowe 2 kroki (zdefiniowanie effect creatora oraz napisanie funkcji, która zamienia efekt w obserwable/promise). Więc niby "więcej boilerplate'u" (więcej o pewnie jakieś 3-5 linijek), ale mimo wszystko czuję, że to będzie bardziej skalowalne.

Z drugiej strony dzięki takiemu parametrycznemu podejściu łatwiej można by było kontrolować całą aplikację (chcę zrobić też pattern matching dla efektów podobny do tego, który zrobiłem dla akcji). I cały projekt nabrał by pewnej spójności (ponieważ efekty by się obsługiwało analogicznie do akcji).

Ale jeszcze zobaczę, może da się do tego jeszcze w inny sposób podejść?

Teoretycznie mógłbym nie usuwać żadnego ficzera i zostawić zarówno parametryczne efekty jak i wprost zwracanie obserwabli/asynchronicznych funkcji wprost z reducerów, ale to już nie chodzi o technikalia (pod kątem implementacji mogą istnieć obydwie te opcje, nie ma problemów), tylko raczej o to, że:

"There should be one-- and preferably only one --obvious way to do it."

Tak to ludzie nie będą wiedzieli, jak mają robić, ew. zaczęliby stosować mało skalowalne design patterny w dużych projektach. Jak coś można zrobić to ludzie to zwykle robią bez zastanowienia się czy to jest właściwe. Widać co się stało z React. Ludzie do takiego componentDidMount wrzucają kompletnie wszystko, łącznie z waleniem requestów przez axiosa czy wrzucaniem tony rzeczy, które w komponentach nie powinny się znajdować. Redux-thunk też jest podobnie nadużywany i thunki to zwykle takie śmietniczki na kod.

Chciałbym jednak, żeby moja biblioteka pomagała ludziom pisać bardziej skalowalny kod.

Komentarze

Popularne posty z tego bloga

Dlaczego nie da się nadgonić frontendu

Absurdy Rekrutacji 2023

Przygody juniora (1)