Skoro tworzę system e-commerce, to oczywistym jest, że po złożeniu zamówień coś zaczyna się dziać. Jest to funkcjonalność kluczowa i posunąłbym się do stwierdzenia, że najważniejsze procesy w aplikacjach związanych z e-commerce dzieją się dookoła realizacji wpływających zamówień. Wypadałoby więc coś o tym opowiedzieć.
W poprzednim poście napisałem, że do reprezentacji statusu zamówienia użyłem enuma. Ale zacznijmy od tego, czym jak właściwie rozumieć status zamówienia. Otóż jest to właściwość klasy Order, która reprezentuje etap realizacji na jakim znajduje się realizacja. Jeśli robicie zakupy przez internet, to najprawdopodobniej dostajecie maile z informacją o przyjęciu zamówienia, jego skompletowaniu, wysłaniu, a czasem na sam koniec podziękowanie i zaproszenie do ponownej współpracy. Klient lubi być informowany, o tym co dzieje się z rzeczami, które kupił.
Jednak status ma rolę o wiele większą niż informacyjna i jego zmiany wiążą się z wieloma czynnościami, które dzieją się niejako dookoła. Wysyłane są maile o zmianach statusu, zmieniane są stany magazynowe itd. Gastronomia jest o tyle specyficzną branżą, że o ile w pewnych obszarach zachodzi o wiele mniej procesów i są one prostsze, to w innych miejscach wymaga specjalnego podejścia. Stąd też musiałem dobrze przemyśleć kontekst, w któym będzie znajdował się YumYum Commerce.
Zacznijmy od tego, że nikt nie będzie zatrudniał do restauracji specjalisty, do obsługi zamówień internetowych. Najczęściej obsługą panelu administracyjnego będzie zajmowała się osoba nieposiadająca specjalistycznej wiedzy i dla takich osób trzeba przygotować cały interfejs. Czasem może to być osoba, która nie do końca wie, że istnieją różne przeglądarki i na wszystkie mówi “Internet”. Dlatego też za kolejne etapy realizacji zamówienia odpowiada jeden przycisk i jeden kontroler, którego zadaniem jest zwiększenie statusu zamówienia o jeden (dlatego właśnie enum jest idealnym rozwiązaniem) i wysyła referencję zamówienia do obiektu OrderProcessor, który w zależności od obecnego statusu wywołuje odpowiednie metody i kieruje procesami, które wiążą się z każdą zmianą.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public interface IOrderProcessor { void ProcessOrder(Order order); } public enum OrderStatus { New, Ordered, Accepted, Sent, Done, Cancelled } |
Implementacja interfejsu IOrderProcessor przyjmuje przychodzącą referencję do zamówienia, sprawdza jej status i w zależności od tego jaki on jest, wywołuje jedną z metod, która zajmuje się odpowiednimi na danym etapie czynnościami, które na razie są swego rodzaju zaślepkami, które będę wypełniał nową funkcjonalnością. W tej chwili wygląda implementacja wygląda następująco.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
public void ProcessOrder(Order order) { switch (order.Status) { case OrderStatus.New: onOrderNew(order); break; case OrderStatus.Ordered: onOrderOrdered(order); break; case OrderStatus.Accepted: onOrderAccepted(order); break; case OrderStatus.Sent: onOrderSent(order); break; case OrderStatus.Done: onOrderDone(order); break; case OrderStatus.Cancelled: onOrderCancelled(order); break; } } void onOrderNew(Order order) //Invoked on creation of new order { order.Status++; ProcessOrder(order); Debug.WriteLine("OnOrderOrdered executed"); } void onOrderOrdered(Order order) { //TODO: Send email about order to customer //TODO: Send notification to admin desktop Debug.WriteLine("OnOrderOrdered executed"); } void onOrderAccepted(Order order) { //NYI Debug.WriteLine("OnOrderAccepted executed"); } void onOrderSent(Order order) { //NYI Debug.WriteLine("OnOrderSent executed"); } void onOrderDone(Order order) { //NYI Debug.WriteLine("OnOrderDone executed"); } void onOrderCancelled(Order order) { //NYI Debug.WriteLine("OnOrderCancelled executed"); } |
Dzięki uproszczeniu całego procesu mogę pod pojedynczy przycisk podpiąć metodę akcji NextStatus, a zaakceptowanie, zakończenie czy wysłanie zamówienia w drogę do klienta można obsłużyć w bardzo prosty sposób, który w razie braku większych komplikacji sprowadza się do kilku kliknięć. Samo wywołanie odpowiednich metod i rozpoczęcie procesów jest niemożliwe jeśli zamówienie zostało wcześniej anulowane lub zakończone, zmiana statusu dla takich zamówień oczywiście musi być możliwa, ale nie ma takiej potrzeby, by dało się to robić w równie bezmyślny i automatyczny sposób jak całą resztę.
1 2 3 4 5 |
if (order.Status != OrderStatus.Done && order.Status != OrderStatus.Cancelled) { order.Status++; processor.ProcessOrder(order); } |
Co osiągnąłem w ten sposób? Znaczne uproszczenie procesu obsługi zamówienia od strony kodu, bo wszystko jest obsługiwane przez jedną metodę, która pod spodem robi swoje, a jak przyjdzie mi do głowy na przykład wysyłanie SMS z powiadomieniem o wysyłce zamówienia, to jedyne co muszę zrobić to podpiąć odpowiedzialną za SMS metodę onOrderSent. Z drugiej strony taka konstrukcja pozwala mi uprościć procedurę obsługi zamówień do jednego przycisku, co pozwoli przyjmować i realizować je z poziomu głównego pulpitu w panelu administracyjnym (który jest na etapie planowania).
A co ciekawego może wiązać się ze zmianą statusu zamówienia? Mam kilka pomysłów i chętnie opowiem o nich za kilka dni, kiedy to podsumuję pierwszy miesiąc pracy nad projektem i wejście w etap, w którym projekt znajduje się już niemalże w fazie Minimum Viable Product.