Zastanawiam się nad stworzeniem nowego rozwiązania, które z natury jest bardzo modułowe i chciałbym stworzyć strukturę obsługującą ten projekt, aby umożliwić łatwą rozbudowę w przyszłości, wyraźne rozdzielenie problemów, licencjonowanie według modułów itp. Większość tego, co mam znalezione w Internecie informacje o aplikacjach modułowych lub złożonych są zorientowane na interfejs użytkownika, koncentrują się na Silverlight, WPF itp. W moim przypadku opracowuję aplikację usługi WCF, która będzie używana przez innych programistów pracujących nad różnymi projektami interfejsu użytkownika.
tło
Celem mojego projektu jest stworzenie scentralizowanego źródła logiki biznesowej / domeny do obsługi kilku naszych aplikacji biznesowych, które obecnie duplikują procesy, reguły itp. I choć nie wszystkie korzyści wynikające z modułowości zostaną osiągnięte, chciałbym wykorzystać możliwość ustanowienia ram, które wykorzystają te części, z których możemy skorzystać.
Rozpocząłem projektowanie od spojrzenia na interfejs API udostępniony przez aplikację serwisową. Oczywiste jest, że usługi można podzielić według modularnych linii, które rozważałem. Na przykład będę mieć usługi FinanceService, InventoryService, PersonnelService itp., Które grupują moje operacje usługowe, aby zapewnić wysoką spójność w interfejsie API przy jednoczesnym utrzymaniu niskiego poziomu sprzężenia, tak aby klienci musieli korzystać tylko z usług związanych z ich aplikacją.
Ma dla mnie sens, że mogę mieć osobne moduły dla każdej z tych usług, takie jak MyApp.Finance, MyApp.Inventory, My.Personnel i tak dalej. Zagadnienia przekrojowe i typy udostępnione byłyby we wspólnym zestawie MyApp. Stąd jestem trochę związany.
(Och, powinienem wspomnieć, że użyję kontenera IoC do Dependency Injection, aby utrzymać luźne powiązanie aplikacji. Nie wspomnę o którym, ponieważ nie chcę otwierać pudełka Pandory!)
W MyApp.ServiceHost utworzę plik hosta usługi (.svc) odpowiadający każdemu modułowi, na przykład FinanceService.svc. Host usługi potrzebuje nazwy usługi, która odpowiada informacjom w pliku konfiguracyjnym zawierającym interfejs definiujący umowę serwisową. Konfiguracja IoC jest następnie używana do mapowania konkretnej implementacji interfejsu do użycia.
1. Czy warstwa usługi powinna implementować interfejs API i przekazywać moduły, czy moduły powinny być samodzielne (ponieważ zawierają wszystko, co związane z tym modułem, w tym implementację usługi)?
Jednym ze sposobów rozwiązania tego problemu jest posiadanie „modułu” MyApp.Services, który zawiera realizację umów serwisowych. Każda klasa usług po prostu deleguje do innej klasy w odpowiednim module, który zawiera logikę domeny dla operacji. Na przykład klasa WCS FinanceService w MyApp.Services deleguje do innego interfejsu, który jest zaimplementowany w module Finance w celu przeprowadzenia operacji. Pozwoliłoby mi to zachować cienką fasadę usługi i „wtyczkę” implementacji do implementacji usługi i wyeliminować konieczność martwienia się modułów na przykład o WCF.
Z drugiej strony, być może preferowane jest, aby każdy moduł był samodzielny, ponieważ ma interfejs i implementację. Host usług odnosi się do interfejsu umowy serwisowej znalezionego w module, a IoC jest skonfigurowany do korzystania z odpowiedniej implementacji również z modułu. Oznacza to, że dodawanie nowego modułu można wykonać bez żadnych zmian w warstwie usług oprócz dodania nowego pliku .svc i informacji konfiguracyjnych IoC.
Zastanawiam się nad tym, czy przejdę ze standardowego WCF na interfejs usługi RESTful, a może przejdę do usług RIA lub czegoś takiego. Jeśli każdy moduł zawiera realizację umowy serwisowej, muszę wprowadzić zmiany w każdym module, jeśli zmienię technologię lub podejście serwisowe. Ale jeśli fasada jest własnym modułem, muszę tylko zamienić tę część, aby dokonać zmiany. Moduły musiałyby wówczas zaimplementować inny zestaw umów (interfejsów), prawdopodobnie zdefiniowany we wspólnym zestawie ???
2. Jaki jest najlepszy sposób obsługi współdzielenia zasobów między modułami i / lub zależności między modułami?
Weźmy na przykład operację odbierania. Na pierwszy rzut oka sensowne jest, że trafia ono do modułu zapasów, ponieważ przyjmowanie towarów jest funkcją zapasów. Istnieje jednak również aspekt finansowy, ponieważ musimy wygenerować pokwitowanie i autoryzować płatność.
Z jednej strony spodziewałbym się, że użyję pewnego rodzaju zdarzeń / wiadomości w domenie do komunikowania operacji. Moduł zapasów wywołuje towary, które są obsługiwane przez moduł finansowy w celu wygenerowania pokwitowania i zainicjowania procesu płatności. Oznacza to jednak, że moduł finansowy musi wiedzieć o otrzymanych pozycjach magazynowych. Mogę po prostu odnieść się do nich według identyfikatora, ale co, jeśli potrzebuję dodatkowych informacji dotyczących paragonu, takich jak nazwa i / lub opis, koszt jednostkowy itp.? Czy ma sens, aby każdy moduł miał własną wersję elementu ekwipunku, który został zaprojektowany w celu zaspokojenia potrzeb tego modułu? W takim przypadku moduł finansowy musiałby samodzielnie sprawdzić pozycje zapasów podczas obsługi towarów otrzymanych.
...
Użyłem tego przykładu celowo, ponieważ dużo pracowałem z różnymi systemami ERP i wiem, że są one zaprojektowane z tego rodzaju modułowością - po prostu nie wiem jak. Nie wspominam też wyraźnie o tym powyżej, ale wolę podążać za zasadami projektowania opartego na domenie podczas projektowania rozwiązania i wierzę, że ten typ modułowości pasuje właśnie do tego obszaru.
Każda pomoc w owinięciu mojej głowy jest bardzo doceniana.