wzorzec do udostępniania obiektów między API a aplikacją

9

Mam poważne wątpliwości co do projektu mojej aplikacji internetowej.

Chciałem oddzielić logikę biznesową od interfejsu, dlatego stworzyłem interfejs API sieci Web, który obsługuje wszystkie żądania do bazy danych.

Jest to interfejs API sieci Web ASP.NET z platformą Entity oraz jednostką pracy i ogólnym wzorcem repozytorium. Jak dotąd wszystko jest dobrze.

PROBLEM

Potrzebuję pomocy, ale nie mogę znaleźć skutecznego sposobu udostępniania obiektów między interfejsem API a aplikacją.

Nie chcę serializować bezpośrednio obiektu encji, pomyślałem, że byłoby to złą praktyką, ponieważ jeśli zmieni się model encji, mogę skończyć serializacją dużych obiektów bez powodu.

Jak to teraz zaimplementowane

Ponieważ moim interfejsem jest aplikacja sieci Web ASP.NET w języku C #, a mój interfejs API jest w języku C #, stworzyłem wspólną bibliotekę z definicją wszystkich moich klas, które chcę udostępniać między nimi.

Wiem, że to rozwiązanie nie zadziała, kiedy opracuję aplikację na Androida. Będę musiał ponownie utworzyć klasy w Javie, ale to nie jest mój największy problem.

Problem polega na tym, że mam wrażenie, że zawsze przekształcam swoje obiekty.

PRZYKŁAD

Oto przykład mojego przepływu pracy:

Zaczynam od modelu zawierającego wszystkie obiekty i adnotacje danych dla mojego formularza, a następnie użytkownik wysłałby ten model do kontrolera.

W kontrolerze muszę przekonwertować ten model na klasę we wspólnej bibliotece, a następnie wysłać ten obiekt do mojego interfejsu API.

Następnie kontroler w moim interfejsie API przechwytuje wywołanie i przekształca ten obiekt w obiekt encji, aby zaktualizować bazę danych.

Mam więc 3 klasy

  1. Model widoku ze wszystkimi adnotacjami danych do sprawdzania poprawności (klient)
  2. Wspólne klasy bibliotek do udostępniania obiektów (DLL)
  3. Klasy encji (API)

Mam wrażenie, że robię coś naprawdę nie tak. Czy jest coś bardziej eleganckiego? Chciałbym upewnić się, że mam dobre rozwiązanie tego problemu, zanim projekt stanie się zbyt duży.

Marc
źródło
Jeśli moje pytanie nie jest jasne, nie wahaj się zadawać pytań.
Marc
Dla mnie nie jest jasne, jaką architekturę wdrożyłeś (może to mnie intryguje). Czy jest to architektura 3-warstwowa: klient, serwer, db?
Andy
Tak Mam aplikację internetową, która korzysta z interfejsu API sieci Web. Interfejs API to logika biznesowa z bazą danych.
Marc

Odpowiedzi:

12

Wiem, że może się wydawać, że cały czas konwertujesz obiekty do przodu i do tyłu między obiektami bazy danych, obiektami przesyłania danych, obiektami klienta z logiką sprawdzania poprawności i tak dalej, ale powiedziałbym, że nie, nie robisz nic złego .

Każdy z tych obiektów może reprezentować tę samą jednostkę informacji, ale mają bardzo różne obowiązki. Obiekt bazy danych jest interfejsem komunikacyjnym z bazą danych i powinien być przechowywany w warstwie bazy danych, ponieważ może zawierać różne adnotacje metadanych bazy danych i / lub niepotrzebne szczegóły dotyczące implementacji bazy danych.

Obiekt do przesyłania danych stanowi interfejs komunikacyjny z użytkownikami interfejsu API. Powinny być tak czyste, jak to możliwe, aby ułatwić korzystanie z różnych języków / platform. Może to nakładać pewne ograniczenia na ich wygląd i zachowanie w zależności od tego, jakich klientów interfejsu API chcesz obsługiwać.

Obiekty klienta z logiką sprawdzania poprawności tak naprawdę nie są częścią twojego projektu API, są częścią twojego projektu konsumenckiego. W tym przypadku nie mogą być one identyczne z obiektami przesyłania danych, ponieważ dodajesz na nich dodatkową logikę specyficzną dla klienta (w tym przypadku atrybuty sprawdzania poprawności), o której serwer nic nie wie (i nie powinien nic wiedzieć!) policz te obiekty jako część interfejsu API, ponieważ tak naprawdę nie są. Są one bardzo specyficzne dla aplikacji konsumenckich, a niektóre aplikacje wykorzystujące interfejs API mogą nawet nie musieć tworzyć tych obiektów i równie dobrze mogą przetrwać tylko na obiektach do przesyłania danych. Na przykład, jeśli nie potrzebujesz żadnej weryfikacji, nie potrzebujesz dodatkowej warstwy obiektów, które są całkowicie identyczne z obiektami do przesyłania danych.

Wydaje mi się, że każdy z trzech typów obiektów bardzo ładnie mapuje się na jedną odpowiedzialność, jaką jest czyste kodowanie i dobra praktyka. Niestety, czysty kod i dobre praktyki czasami oznaczają, że piszesz dużo dodatkowego kodu i przeskakujesz dodatkowe kółka „tylko dlatego”. Podczas kodowania może być trudno docenić wartość, jaką to daje - ale jak tylko wydasz aplikację i zaczniesz ją obsługiwać lub dodawać nowe funkcje do następnej wersji, prawdopodobnie zaczniesz doceniać, że poświęciłeś czas na w pierwszej kolejności należy odpowiednio rozdzielić te obawy. (Nie wspominając o tym, że ty

Nienawidzę także pisania kodu konwersji między różnymi typami obiektów, jak ten, ale moje rozwiązanie jest zwykle jedno z następujących:

  • Skorzystaj z biblioteki, która wykonuje większość operacji podnoszenia ciężarów z konwersją obiektów - na przykład, jeśli używasz C #, możesz użyć fantastycznej biblioteki AutoMapper ( http://automapper.org/ ). Sądzę, że istnieje kilka innych bibliotek tego typu, ale AutoMapper jest najmocniejszą biblioteką, jaką do tej pory widziałem.
  • Jeśli nie możesz znaleźć biblioteki, która pomoże ci w konwersji obiektów, napisz zestaw narzędzi do konwersji między nimi. Może to być do kitu, ale na dłuższą metę warto, napisz metodę konwersji za pierwszym razem, gdy musisz coś przekonwertować - nie czekaj.
wasatz
źródło
Dziękuję za wyjaśnienia, ale wciąż trudno mi coś zrozumieć. Nie rozumiem, dlaczego warstwa do przesyłania danych nie ma żadnej weryfikacji? Co się stanie, jeśli zapomnę o sprawdzeniach mojej następnej aplikacji mobilnej? Przynajmniej nie sprawdziłbym, kiedy wywołuję API zamiast robić wyjątek w moim modelu bazy danych. Nie jestem pewien, czy rozumiem.
Marc
1
Nie twierdzę, że nie powinieneś sprawdzać poprawności na poziomie interfejsu API. Szczerze mówiąc, jest to najważniejsze miejsce, w którym można się sprawdzić. Sprawdzanie poprawności w aplikacji to po prostu „fajna funkcja”, która pomaga użytkownikom nie popełniać błędów, a sprawdzanie poprawności obiektów do przesyłania danych służy do ochrony przed złośliwymi i błędnymi danymi. Ponieważ są to różne przypadki użycia jednak może trzeba użyć różnych ram walidacji (trzeba będzie używać różnych ram walidacji jeśli aplikacja a api nie jest napisana w tym samym języku) i można potwierdzić nieznacznie różne rzeczy na każdym poziomie (cd w następnym komentarzu)
wasatz
1
Dlatego powinieneś zweryfikować swoje obiekty do przesyłania danych. Ale powinieneś również upewnić się, że sposób ich sprawdzania nie spowoduje przypadkowego wprowadzenia żadnych zależności od innych ram . I oczywiście, jak powiedziałem wcześniej, naprawdę nie możesz być pewien, że twoje obiekty do przesyłania danych zostały w ogóle sprawdzone lub że zostały one sprawdzone w tej samej strukturze - więc musisz „sprawdzić dwa razy”.
wasatz
2
Przede wszystkim powinieneś spróbować zobaczyć swoją aplikację i interfejs API jako dwie zupełnie różne i osobne aplikacje. Być może rozwijacie je w tym samym czasie, a oni mogą być w tym samym projekcie wizualnym / projekcie eclipse. Ale tak naprawdę są to dwa całkowicie oddzielne programy. Podczas pracy nad aplikacją postaraj się „zapomnieć”, że to Ty stworzyłeś interfejs API i używaj go tak, jak w przypadku normalnego interfejsu API innej firmy. W ten sposób będziesz mieć większą szansę zobaczyć, jak będą się czuć inni, gdy korzystasz z Twojego API i poprawiaj najgorsze części na wczesnym etapie.
wasatz
1
To samo oczywiście dotyczy pracy nad projektem API. Spróbuj wyobrazić sobie, że piszesz usługę, z której skorzysta wielu programistów zewnętrznych. Staraj się nie myśleć zbyt dużo o bieżącej aplikacji, ale skup się bardziej na tym, „jakie usługi zapewniam” i zakładając, że wszyscy, którzy używają twojego API (w tym ty), są złymi ludźmi, którzy próbują zabić twój serwer i sprawi, że usuniesz całą bazę danych.
wasatz