Obiekty domenowe z kilkoma stanami

0

Hej, załóżmy, że mamy sobie obiekt Zamówienie i posiada on kilka różnych stanów - CREATED, PAID, CANCELED, SENT, CONFIRMED itd.

Standardowym podręcznikowym zamodelowaniem jest zrobienie sobie encji Order i enuma z tymi wartościami. Wtedy w większości metod biznesowych mamy ifa/switcha na te stany i robimy odpowiednią logiką. Dużo też rzucamy IllegalStateException z racji tego, że np z CREATED nie przejdziemy do CONFIRMED.

Jakie macie lepsze podejście do tego tematu?
Być może zamiast obiektu Order mieć obiekty CreatedOrder, PaidOrder, CanceledOrder itd?
Wtedy np CreatedOrder ma metodę pay(), która produkuje obiekt PaidOrder, który normalnie potem persystujemy.

Dzięki temu pozbywamy się ifologii na statusy zamówienia, bo każdy ma metody zdefiniowane pod siebie. Minus taki, że musimy mieć tabelkę per kazda z tych klas.

1
  • Niektórzy robią maszynę stanów - są liby które wszystko za ciebie ogarną. Problem: ORM (jak robisz microORM lub mapowanie ręczne to może być to)
  • Rozbicie jakie się najczęściej widuje to np. niekompletny obiekt - formularz zmaównienia nie do konca wypełniony, oraz pełne i zwalidowane już zamówienie np. PreOrder, Order
  • Generalnie dał bym order + enum, no chyba że sent order ma znacznie więcej pól/inne pola niż zwykły order (ale zazwyczaj nie ma).

Piszę wieczorem zmęczony, więc nie traktuj tego jako super rady.

Można też podejść do tego jako proces i mieć encje order, payment, shipment, oraz encję modelującą proces OrderFlow która była by powiązana z pozostałymi. To podejście dosyć dobrze działało by userwisami + orchestrator procesu.

2

@Bambo:

zamiast obiektu Order mieć obiekty CreatedOrder, PaidOrder, CanceledOrder itd

To brzmi jak dobry pomysł. W nowszych javkach będzie lepiej wspierane przez sealed class (a Kotlin i Scala mają to od dawna).

Minus taki, że musimy mieć tabelkę per kazda z tych klas.

But why? To, że to różne klasy to nie znaczy, że muszą być w osobnych tabelkach.

0

Z wieloma obiektami jest jeszcze taki kłopot że przy przejściu trzeba usunąć poprzedni obiekt, co wtedy zrobisz z historią zmian itp?
Rozwiązanie żeby nie usuwać ma znów ten problem że nie będzie centralnego miejsca które mówi w jakim stanie jest order...

0

@0xmarcin:

Dlaczego modeluje to samo? Masz inne metody na każdym z tych obiektów. Co innego możesz zrobić z nowym zamówieniem, a co innego z zapłaconym. Możesz to nawet nazwać inaczej niż OrderX. Ja właśnie myślę, że to bardziej DDD. Uber Order encja modeluje strukturę danych, a nie proces biznesowy. Do każdego z tych obiektów zaciągniesz te dane, które są potrzebne na zrobienie biznesu i nie więcej. Jeden wielki obiekt łamie SRP i ogólną spójność klasy.

0

Ja bym się zastanowił jak to wygląda.

Jeśli Order jest sprawdzany "z zewnątrz" tj.

    if (order.getState().equals(State.CANCELLED)) {
        throw new OrderCancelledException();
    }

to bym trzymał ciągle to w enumie.

Jeśli Order ma w sobie jakąś logikę, np.

    if (order.getClientCost()) { // 0.0 dla CANCELLED, 45.0 dla OPEN 
        // ...
    }

to dorzuciłbym dziedziczenie.

Czyli po prostu sprawa się sprowadza do tego czy to jest już obiekt domenowy, czy jakiś rekord z danymi.

Być może zamiast obiektu Order mieć obiekty CreatedOrder, PaidOrder, CanceledOrder itd?

Takie podejście ma jeden wielki plus - pozwala na jasne zdefiniowanie zależności. Minus jest taki, że możesz zostać z dwoma zamówieniami - np. otwartym i opłaconym.

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