Pobieranie danych z zewnętrznego API oraz zapis do bazy danych (MVC)

0

Witam, stworzyłem aplikację w MVC, która pobiera aktualne kursy walut z zewnętrznego API, wyświetlam je jako tabelka na stronie oraz zapisuję te dane do bazy za pomocą seedera.

W folderze Services, stworzyłem serwis (CurrencyService.cs), w którym pobieram dane z API i następnie przekazuje je do kontrolera (CurrencyController.cs), następnie wyświetlam dane w widoku (Index.cshtml). Dane pobrane z API zapisuje do bazy danych za pomocą seedera (CurrencySeeder), który znajduje się w folderze Data.

Czy sposób w jaki to zrobiłem jest odpowiedni, czy da się coś poprawić?
Endpointy (A, B, C):
https://api.nbp.pl/api/exchangerates/tables/A
https://api.nbp.pl/api/exchangerates/tables/B
https://api.nbp.pl/api/exchangerates/tables/C

Link do kodu: https://github.com/bartek101119/Currency_exchange

Dziękuję za wszelkie porady :)

Edit:
Czy w poprawny sposób wyświetlam tabelkę link? Dane nad tabelką mogą być w h5?

1

Jak już do zamiast WebClient'a używamy HttpClient
Link do Api NBP trzymamy w ustawieniach a nie zahardkodowany
Asynchroniczność.
Zamiast double użyłbym decimal do wartości kursu

0
szydlak napisał(a):

Jak już do zamiast WebClient'a używamy HttpClient
Link do Api NBP trzymamy w ustawieniach a nie zahardkodowany
Asynchroniczność.
Zamiast double użyłbym decimal do wartości kursu

Dziękuję za wskazówki. Dodałem poprawki do mojego kodu, są widoczne na githubie :)
Co myślisz o takim podejściu zapisywania danych do bazy, za pomocą seedera?

Edit:
Zmieniłem HttpClient na IHttpClientFactory

0

Czy w poprawny sposób wyświetlam tabelkę tabelka cshtml? Dane nad tabelką mogą być w h5?

1
kosciuszkobest napisał(a):

Czy sposób w jaki to zrobiłem jest odpowiedni, czy da się coś poprawić?

Nie ma czegoś co da się nazwać kodem idealnym więc poprawiać można w nieskończoność, należy sobie zadać pytanie czy się to opłaca. Tutaj chyba jeszcze tak.

Metoda Index kontrolera ma zahardkowaną tabelę C. Dlaczego nie przekażesz tego z frontu? Poza tym, z punktu widzenia użytkownika to nie wiem czy ma to wielki sens. Gdybym to ja odpalał aplikację to chciałbym zobaczyć pewnie całą listę lub jakąś konkretną walutę albo ich zestaw, Więc na twoim miejscu pobrałbym wszystkie a potem przedstawiał użytkownikom w określonym formacie.

Co do samego pobierania i zapisu - CurrencySeeder pobiera kursy tylko wtedy kiedy nie ma niczego w bazie. To znaczy, że po godzinie, na drugi dzień czy za miesiąc aplikacja będzie przechowywać stare, już dawno nieaktualne kursy. Co ciekawe, kontroler pobiera kursy uzywając implementacji ICurrencyService z całkowitym pominięciem bazy. Może lepiej wykonać zapis i przechowywać historię kursów? A skoro masz bazę - dlaczego nie sprawdzić czy to co zostało już pobrane i jest w bazie danych jest jeszcze aktualne i jeśli tak to zwrócić najnowszy zestaw z bazy (a jeśli nie to strzelić do API NBP, pobrać nowe, zapisać i zwrócić?).

Co do samego trybu pobierania - jeśli ta apka ma działać bez przerw to może zamiast uruchamiać zadanie pobierania kursów w czasie jej uruchomienia to byłoby lepiej zaimplementować BackgroundService który będzie robić to cyklicznie (https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-6.0&tabs=visual-studio)?

ICurrencyService - po co ci ten interfejs? Z tego co widzę to nie jest on ani trochę potrzebny, przynajmniej w stanie obecnym.

Brak testów - testować możesz zarówno backend jak i logikę w js (tego akurat nie używasz), może też jakieś testy UI?

Katalog Models - dlaczego trzymasz modele bazodanowe z view modelem reprezentującym błąd? Dlaczego kontroler zwraca typ bazodanowy zamiast wyłącznie tego co jest potrzebne na widoku?

Poza tym pomyśleć o rozbiciu klas na mniejsze, tak aby miały wyłącznie jedną odpowiedzialność. Na przykład CurrencyService, tutaj mamy pobieranie konfiguracji (o tym w ogóle za chwilę), tworzenie urla, pobranie odpowiedzi i jej deserializacja. Konfigurację można przekazać w dużo lepszy sposób, wstrzyknąć jako konkretny typ utworzony z IConfiguration albo użyć patternu IOptions (https://docs.microsoft.com/en-us/dotnet/core/extensions/options). Sama obsługa sprawdzenia poprawności odpowiedzi (czego u ciebie nie ma) i jej deserializacja może być zaimplementowana generycznie. Pobranie danych z api nbp również. W ten sposób CurrencyService byłby okrojony wyłącznie do jednej, jedynej odpowiedzialności a mianowicie definiowania procesu. Sam w sobie nie robiłby nic innego jak definiował sekwencję wywołania metod różnych klas tak aby ostatecznie zwrócić to co ma zostać zwrócone. A te mniejsze klasy zajmowałyby się właśnie pobieraniem danych, obsługą i deserializacją odpowiedzi, generowanie odpowiedniego urla też raczej warto wyciągnąć do dedykowanego typu.

CurrencySeeder - nie wiem czy warto wykonywać metodę _context.Database.CanConnectAsync() bo można po prostu spróbować wykonać odczyt. Tu ponownie wraca problem braku obsługi błędów.

Zastanawiałeś się nad dodaniem logów?

Generalnie można tu zrobić jeszcze całkiem sporo, jeśli to apka na której się uczysz i nie masz parcia na pisanie czegoś nowego to warto w tą zainwestować trochę czasu.

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