Czysta architektura to jedno z fundamentalnych pojęć w rozwoju aplikacji. W prostych słowach, oznacza to sposób organizacji kodu i określenia zależności między poszczególnymi jego częściami. Chociaż nie istnieje jeden „idealny” sposób implementacji czystej architektury, to w tym rozdziale przedstawię moją perspektywę, która wykształciła się przez lata praktyki. Moja wizja czystej architektury opiera się na doświadczeniach w tworzeniu aplikacji API, szczególnie o średnim stopniu złożoności. W przypadku bardziej skomplikowanych systemów architektura wymaga większej uwagi, ale moja koncepcja pomoże Ci zrozumieć podstawy i wdrożyć je w 99% przypadków, które napotkasz w swojej pracy jako programista API.
Kluczowym założeniem czystej architektury jest niezależność poszczególnych warstw aplikacji. Niezależność oznacza w tym przypadku brak silnych zależności między technologiami, źródłami danych zewnętrznych oraz między warstwami aplikacji. Niezależność jest fundamentem dobrze zaprojektowanej aplikacji, co pozwala na łatwiejsze jej rozwijanie i utrzymanie w przyszłości.
Po pierwsze, warstwy aplikacji muszą być niezależne od interfejsu użytkownika. Oznacza to, że interfejs (np. API, aplikacja desktopowa) nie powinien w żaden sposób wpływać na logikę biznesową czy dostęp do danych. Zastosowanie odpowiednich abstrakcji w kodzie pozwala na izolację logiki biznesowej od technologii interfejsu użytkownika.
Po drugie, aplikacja powinna być niezależna od bibliotek i frameworków. Zbyt mocne powiązanie z konkretnym narzędziem powoduje, że stajemy się od niego zależni, co może ograniczać możliwości rozwoju i utrzymania aplikacji w przyszłości. Kluczem do uniknięcia tej pułapki jest stosowanie abstrakcji, które pozwalają na wymianę frameworków bez konieczności zmiany całego systemu.
Po trzecie, aplikacja powinna być niezależna od dostępu do danych zewnętrznych. Możliwość zmiany bazy danych lub sposobu jej dostępu (np. przejście z bazy SQL na XML) musi być możliwa bez ingerencji w logikę biznesową aplikacji. To oznacza, że warstwa dostępu do danych musi być odpowiednio zaprojektowana, aby wspierać łatwą wymianę technologii.
Po czwarte, aplikacja musi być testowalna w izolacji. Oznacza to, że testowanie jednostkowe (unit testing) nie powinno wymagać zależności od innych warstw aplikacji ani technologii. Dzięki zastosowaniu odpowiednich technik, takich jak dependency injection, możemy przetestować logikę biznesową bez potrzeby uruchamiania bazy danych czy korzystania z zewnętrznych bibliotek.
Moje podejście do czystej architektury zakłada podział aplikacji na co najmniej cztery główne warstwy:
-
Warstwa domeny - zawiera wszystkie obiekty domenowe, interfejsy repozytoriów (dostępu do danych) oraz interfejsy serwisów. Jest to warstwa, która nie zależy od żadnej innej warstwy, stanowiąca „serce” aplikacji. W tym miejscu znajdują się wszystkie abstrakcje i kontrakty aplikacyjne.
-
Warstwa prezentacji - w kontekście tej książki jest to warstwa aplikacji ASP.NET Core, która udostępnia API przez HTTP. Ta warstwa zależy od wszystkich innych warstw, ponieważ konieczne jest zdefiniowanie, które klasy implementują jakie abstrakcje w konfiguracji aplikacji (np. w pliku Program.cs).
-
Warstwa logiki biznesowej (lub aplikacyjnej) - implementuje zasady biznesowe oraz orkiestruje działania różnych komponentów odpowiedzialnych za realizację określonych akcji (np. dostęp do danych, logowanie, cache'owanie danych itp.). Ważne, aby ta warstwa zależała wyłącznie od warstwy domeny, a jej kod nie powinien mieć żadnych zależności od technologii (infrastruktury). Wyjątkiem mogą być tylko warstwy genericzne, np. narzędzia programistyczne, które pomagają w codziennej pracy.
-
Warstwy infrastruktury - implementują konkretne technologie, takie jak dostęp do danych (np. SQL, HTTP). Ważne jest, aby te warstwy były niezależne od siebie, a każda z nich powinna implementować tylko jedną technologię. Na przykład, jedna warstwa może obsługiwać dostęp do bazy SQL, a inna - dostęp przez HTTP.
Dodatkowo, warto rozważyć wprowadzenie opcjonalnej warstwy narzędziowej, która może implementować różne generatory, konwertery czy klasy pomocnicze, które są wykorzystywane w innych warstwach, ale nie są związane z żadną konkretną technologią.
Powyższa struktura ma na celu stworzenie aplikacji, której komponenty są maksymalnie od siebie oddzielone i niezależne. Dzięki temu możemy łatwiej rozwijać aplikację, zmieniać jej elementy bez ryzyka naruszenia reszty systemu, a także efektywnie przeprowadzać testy jednostkowe.
Warto także dodać, że czysta architektura nie jest jedynym podejściem do organizacji kodu. Istnieje wiele innych metodologii, takich jak architektura heksagonalna, cebulowa czy Domain-Driven Design (DDD), które również opisują sposób organizowania warstw aplikacji. Choć każda z tych metod ma swoje zalety i specyficzne zastosowanie, to moja wizja czystej architektury, przedstawiona powyżej, będzie wystarczająca do rozwiązywania większości problemów, z jakimi spotkają się programiści API.
Oczywiście, oprócz samej struktury architektonicznej, ważną rolę odgrywają także wzorce projektowe. Choć książka nie ma na celu nauki wzorców projektowych, warto wiedzieć, że mają one duży wpływ na architekturę oprogramowania. Wzorce projektowe są sprawdzonymi rozwiązaniami powszechnych problemów projektowych, które pomagają w organizacji i budowie systemu. Dzięki nim można stworzyć elastyczne, łatwe w utrzymaniu i rozszerzalne aplikacje.
Jak skutecznie zarządzać wersjonowaniem API w kontekście RESTful?
Zarządzanie wersjami API jest kluczowym elementem utrzymania spójności i kompatybilności aplikacji w miarę jej rozwoju. Wersjonowanie pozwala na wprowadzenie nowych funkcji, poprawki błędów i optymalizacje bez ryzyka przerwania działania już istniejących usług i aplikacji zależnych od tego API. Choć REST API jest jednym z najczęściej używanych sposobów udostępniania usług, jego odpowiednie wersjonowanie wymaga dobrze przemyślanej strategii.
Jednym z popularniejszych podejść jest wersjonowanie przez nagłówki HTTP, które pozwala na dostosowanie wersji API bez zmiany struktury URL. Zamiast zmieniać ścieżkę zasobu, wersja może być przesyłana w nagłówkach HTTP, co pozwala na przejrzystość i elastyczność w zarządzaniu. Tego typu rozwiązanie sprawdza się w przypadku, gdy chcemy uniknąć duplikowania tych samych zasobów pod różnymi wersjami API. Nagłówek, który najczęściej jest wykorzystywany do tego celu, to "Accept-Version" lub "X-API-Version". Dla wielu zespołów deweloperskich takie podejście jest wygodne, ponieważ zmiana wersji API nie wymaga dużych modyfikacji w kodzie klienckim, wystarczy odpowiedni nagłówek.
Z drugiej strony wersjonowanie przez trasę (route versioning) jest rozwiązaniem bardziej bezpośrednim i intuicyjnym. W tym przypadku wersja API jest zawarta w samej ścieżce URL, na przykład: /v1/users i /v2/users. To podejście jest szczególnie użyteczne, gdy różne wersje API różnią się znacząco, na przykład w zakresie metod HTTP lub formatu odpowiedzi. Wersjonowanie przez trasę umożliwia łatwiejsze zarządzanie wieloma wersjami tego samego zasobu i pozwala klientom jasno określić, z jaką wersją API chcą współpracować. Ponadto, wersjonowanie w trasie ułatwia monitorowanie zmian i kompatybilności w ramach różnych wersji, co może być istotne w bardziej złożonych systemach.
Przy zarządzaniu wersjami API nie można zapominać o dokumentacji, która powinna być równie dynamiczna jak samo API. Narzędzia takie jak Swagger umożliwiają automatyczne generowanie dokumentacji w zależności od wersji, co znacznie ułatwia pracę z różnymi wersjami API. Dodatkowo, jeśli w danej wersji API wprowadzamy nowe parametry czy zmieniamy sposób obsługi istniejących, powinniśmy odpowiednio oznaczyć to w dokumentacji, by klienci nie mieli wątpliwości co do nowości.
Kiedy API się rozwija i obsługuje różne wersje, warto zadbać o zarządzanie nimi w sposób centralny. Można to zrobić poprzez narzędzia do monitorowania, które pozwalają na śledzenie, które wersje są wciąż aktywnie używane, a które zostały wycofane. Tego rodzaju podejście może pomóc w zminimalizowaniu ryzyka, które wiąże się z obiegiem starych wersji w systemach produkcyjnych, a także ułatwia kontrolowanie migracji do nowych wersji.
Dla deweloperów istotne jest także testowanie wersji API, zwłaszcza w przypadkach, gdy zmiany w wersjach mogą prowadzić do nieoczekiwanych błędów w istniejących aplikacjach. Wykorzystanie narzędzi do automatycznego testowania API, takich jak Postman czy Insomnia, może znacznie ułatwić proces weryfikacji działania wszystkich wersji API. Ważne jest, aby testować zarówno wersje istniejące, jak i nowe, w celu upewnienia się, że integracja z API przebiega bezbłędnie, niezależnie od tego, którą wersję wybierze użytkownik.
Zarządzanie wersjami API nie jest tylko kwestią techniczną, ale także strategiczną. Warto dokładnie rozważyć, jak wprowadzać zmiany, kiedy i dlaczego. Częste aktualizacje wersji mogą prowadzić do chaosu, szczególnie w dużych, rozproszonych systemach, gdzie wiele aplikacji zależy od tej samej usługi. Kluczowym elementem jest tutaj komunikacja z zespołami korzystającymi z API, zapewniając im informacje o planowanych zmianach z odpowiednim wyprzedzeniem. Z kolei z perspektywy użytkowników, dobra dokumentacja i jasno określona strategia wersjonowania pozwolą na płynne przejście do nowych wersji bez ryzyka zakłócenia działania ich aplikacji.
Endtext
Jak zarządzać wersjami API w aplikacji ASP.NET Core 8?
Zarządzanie wersjami API jest kluczowym elementem każdej rozwijającej się aplikacji. W miarę jak projekt rośnie, wprowadzenie nowych wersji API staje się koniecznością, aby zachować kompatybilność z istniejącymi użytkownikami oraz aplikacjami. Istnieją trzy główne metody zarządzania wersjami API: poprzez nagłówki, ścieżki URL oraz zapytania w ciągu zapytań (query strings). W tym artykule skupię się na dwóch najczęściej stosowanych metodach: zarządzaniu wersjami za pomocą nagłówków oraz ścieżek URL.
Wersjonowanie przez Nagłówki
Pierwszą metodą zarządzania wersjami API jest użycie nagłówków HTTP. Jest to jedna z najbardziej eleganckich i czystych metod, ponieważ nie wymaga modyfikacji samego URL. Zamiast tego, wersja API jest przekazywana za pomocą niestandardowego nagłówka, na przykład api-version.
Aby skonfigurować wersjonowanie w aplikacji ASP.NET Core 8, należy zainstalować odpowiednią paczkę NuGet: Asp.Versioning.Http. Po zainstalowaniu tej paczki, możemy skonfigurować wersjonowanie za pomocą rozszerzenia AddApiVersioning. Konfiguracja ta może wyglądać następująco:
W tym przykładzie:
-
DefaultApiVersionustawia wersję domyślną na 1.0, jeżeli w zapytaniu nie zostanie określona wersja. -
ReportApiVersionsumożliwia przesyłanie informacji o dostępnych wersjach w nagłówkach odpowiedzi. -
AssumeDefaultVersionWhenUnspecifiedsprawia, że brak wersji w zapytaniu powoduje użycie wersji domyślnej. -
ApiVersionReaderpozwala wskazać nagłówek, w którym klient ma przesłać wersję API.
Po skonfigurowaniu wersjonowania, możemy zadeklarować dostępne wersje API za pomocą metody NewApiVersionSet. Na przykład:
Wersje 1.0 i 2.0 stanowią zestaw wersji API, który musimy przypisać do odpowiednich punktów końcowych. Możemy to zrobić za pomocą metody WithApiVersionSet:
Zalety wersjonowania za pomocą nagłówków to czystość i elastyczność. Nagłówki są oddzielone od samego URL, co sprawia, że URL pozostaje przejrzysty. Dodatkowo, jeśli nie zależy nam na zmianach w URL, wersjonowanie przez nagłówki jest najczystszym rozwiązaniem.
Wersjonowanie przez Ścieżki URL
Drugą metodą jest wersjonowanie poprzez ścieżki URL. W tej metodzie wersja API jest określana bezpośrednio w samej ścieżce, na przykład /v2/countries. Jest to podejście, które preferuję, ponieważ daje pełną kontrolę nad wersją API w strukturze URL, a jednocześnie jest czytelne i proste do zrozumienia.
Aby zaimplementować wersjonowanie przez URL, możemy używać grup routingu (Route Groups) w ASP.NET Core. Definiujemy grupy wersji API, jak w przykładzie poniżej:
W tym przypadku, wersje 1.0 i 2.0 są definiowane jako oddzielne grupy routingu. Każda z tych grup posiada dedykowane punkty końcowe, a wersja jest określona w samym URL. Używanie tej metody jest stosunkowo łatwe i sprawia, że wersjonowanie API jest bezpośrednio związane z jego strukturą. Może to być bardziej intuicyjne dla programistów, którzy preferują jasne określenie wersji w URL.
Co warto zrozumieć oprócz tego?
Podstawową różnicą między wersjonowaniem przez nagłówki a wersjonowaniem przez ścieżki URL jest wpływ na strukturę aplikacji i wymagania dotyczące zarządzania wersjami. Choć wersjonowanie przez nagłówki może wydawać się bardziej eleganckie i mniej ingerujące w URL, wersjonowanie przez URL jest bardziej intuicyjne i powszechnie stosowane w wielu projektach. Kluczowe jest zrozumienie, że obie metody mają swoje zalety i wady, a wybór jednej z nich zależy od wymagań projektu oraz preferencji zespołu deweloperskiego.
Warto również pamiętać, że wersjonowanie API jest procesem, który należy dobrze zaplanować, aby uniknąć późniejszych komplikacji związanych z aktualizacjami i zmianami w wersjach API. Kluczowe jest nie tylko utrzymanie kompatybilności wstecznej, ale także odpowiednie komunikowanie się z użytkownikami API o dostępnych wersjach oraz metodach przejścia na nowe wersje.
Jak wykorzystać middleware, filtry punktów końcowych i inne funkcje ASP.NET Core 8?
Middleware w ASP.NET Core 8 to potężne narzędzie, które daje niespotykaną elastyczność w zakresie wykonania kodu. Jego działanie opiera się na przepuszczaniu żądań przez kolejne etapy w "rurze" aplikacji. Każdy middleware może wykonywać określoną logikę i przekazać kontrolę do następnego elementu w pipeline. Przykład prostego middleware, jak poniżej, ilustruje, jak dodać funkcjonalność logowania w aplikacji:
Taki fragment kodu wykonuje logowanie na początku i następnie przekazuje kontrolę do kolejnego middleware lub punktu końcowego. Middleware, jak w powyższym przykładzie, jest jednym z najbardziej podstawowych sposobów na dodanie logiki do aplikacji. Jednak ASP.NET Core 8 oferuje jeszcze bardziej zaawansowane mechanizmy, takie jak filtry punktów końcowych, które zapewniają większą kontrolę nad działaniem aplikacji.
Filtry punktów końcowych
Filtry punktów końcowych, wprowadzone w ASP.NET Core 8, dają programistom możliwość dodania logiki przed i po wykonaniu żądania. Dzięki nim możemy np. mierzyć czas wykonywania danego punktu końcowego lub walidować parametry wejściowe bardziej elegancko, niż za pomocą tradycyjnych metod. W poniższym przykładzie mierzymy czas wykonania punktu końcowego:
Warto zauważyć, że filtr punktu końcowego pozwala na elegancką obsługę takich zadań jak logowanie wydajności, bez konieczności modyfikowania samego kodu punktu końcowego. Dodatkowo, filtry mogą być wykorzystywane do bardziej zaawansowanych operacji, takich jak walidacja wejściowych parametrów, co wcześniej wymagało dużej ilości powtarzającego się kodu.
Tworzenie czystego kodu z użyciem filtrów
Dla zachowania czystości kodu, filtry punktów końcowych mogą być enkapsulowane w osobne klasy. Taki sposób pozwala na łatwiejsze zarządzanie logiką filtrów. Przykład klasy LogPerformanceFilter pokazuje, jak łatwo można zaimplementować taką logikę w dedykowanej klasie:
Taki sposób organizowania kodu poprawia jego czytelność i ułatwia testowanie, ponieważ logika odpowiedzialna za pomiar wydajności znajduje się w jednym, wyspecjalizowanym miejscu.
Integracja z FluentValidation
ASP.NET Core 8 pozwala na integrację filtrów punktów końcowych z biblioteką FluentValidation. Dzięki temu można tworzyć filtry, które walidują dane wejściowe w bardziej elegancki sposób. W poniższym przykładzie używamy klasy InputValidatorFilter do walidacji danych wejściowych przed wykonaniem punktu końcowego:
W tym przypadku, przed wykonaniem punktu końcowego, weryfikujemy, czy dane wejściowe spełniają określone zasady walidacji. Jeśli nie, zwracamy odpowiedź zawierającą szczegóły błędu walidacji, co pozwala uniknąć niepotrzebnych wywołań punktów końcowych w przypadku niepoprawnych danych.
Korzyści z używania filtrów
Filtry punktów końcowych i middleware oferują szereg korzyści, które mogą poprawić jakość kodu w aplikacjach ASP.NET Core. Przede wszystkim pozwalają na centralizację logiki, co ułatwia jej testowanie i modyfikowanie w przyszłości. Ponadto, dzięki takim mechanizmom, jak FluentValidation, walidacja danych wejściowych staje się bardziej elegancka, co prowadzi do zmniejszenia duplikacji kodu. Warto podkreślić, że tego rodzaju podejście prowadzi do większej modularności, ułatwia rozwiązywanie problemów związanych z wydajnością aplikacji oraz zapewnia większą przejrzystość w zarządzaniu całą logiką aplikacji.
Rate Limiting i inne zaawansowane funkcje
ASP.NET Core 8 wprowadza również zaawansowane funkcje, takie jak rate limiting, które pozwalają kontrolować liczbę żądań przychodzących do aplikacji w określonym czasie. Dzięki tej funkcji można zminimalizować ryzyko ataków typu DoS (Denial of Service) oraz kontrolować obciążenie serwera w przypadku dużego ruchu. Jest to przykład wykorzystania rozwiązań, które mogą poprawić wydajność aplikacji oraz jej bezpieczeństwo.
Jak efektywnie i bezpiecznie uzyskać dane z API w ASP.NET Core?
W dzisiejszym świecie aplikacji internetowych, dostęp do danych stanowi fundament ich funkcjonalności. W kontekście tworzenia aplikacji w ASP.NET Core, odpowiednie zarządzanie dostępem do danych, zarówno tych przechowywanych w bazach SQL, jak i udostępnianych przez zewnętrzne API, jest kluczowe dla zapewnienia wydajności i bezpieczeństwa. W tym rozdziale przyjrzymy się, jak skutecznie zarządzać dostępem do danych, używając technologii takich jak HttpClient, IHttpClientFactory oraz biblioteki Refit, które znacząco ułatwiają ten proces.
Zaczynając od podstaw, możemy zauważyć, że często potrzebujemy interakcji z danymi, które nie znajdują się w naszej lokalnej bazie danych. W takim przypadku często sięgamy po REST API, które umożliwia dostęp do zasobów przechowywanych zewnętrznie. Właśnie wtedy niezbędnym narzędziem staje się klasa HttpClient. Możemy jej użyć do wysyłania żądań GET, POST, PUT i DELETE w celu interakcji z innymi systemami. Jednak w standardowej formie jej użycie może prowadzić do problemów z wydajnością, szczególnie w przypadku dużej liczby zapytań.
Problemy związane z klasycznym użyciem HttpClient
Kiedy tworzymy nową instancję HttpClient dla każdego zapytania, zmuszeni jesteśmy do tworzenia również nowej instancji HttpMessageHandler, co z kolei prowadzi do wyczerpywania gniazd sieciowych (socket exhaustion). Jest to problem szczególnie widoczny w aplikacjach, które muszą wykonać dużą liczbę zapytań HTTP. Jak więc temu zaradzić? Rozwiązaniem jest użycie IHttpClientFactory.
Optymalizacja z użyciem IHttpClientFactory
IHttpClientFactory to interfejs wprowadzony w .NET Core 1, który pomaga w zarządzaniu cyklem życia instancji HttpClient. Dzięki temu mechanizmowi możemy uniknąć tworzenia wielu niepotrzebnych obiektów HttpClient i zamiast tego wykorzystywać jedną, współdzieloną instancję, co poprawia wydajność aplikacji i zapobiega wspomnianym wcześniej problemom.
Aby skorzystać z tego rozwiązania, należy zainstalować pakiet Microsoft.Extensions.Http, a następnie skonfigurować usługę w pliku Program.cs poprzez dodanie:
Po skonfigurowaniu IHttpClientFactory, możemy przejść do tworzenia specjalnych klas repozytoriów, które będą korzystać z tej technologii. Przykładem może być repozytorium odpowiedzialne za pobieranie flag krajów w formacie obrazów PNG.
Przykład użycia IHttpClientFactory
Poniżej znajduje się przykładowa implementacja klasy MediaRepository, która wykorzystuje IHttpClientFactory do pobierania obrazów flag krajów:
Ta implementacja zapewnia, że instancje HttpClient są odpowiednio zarządzane przez IHttpClientFactory, co wpływa na poprawę wydajności aplikacji, zwłaszcza w przypadku dużej liczby zapytań do API.
Wykorzystanie Refit w celu uproszczenia kodu
Kolejnym krokiem w optymalizacji interakcji z API jest biblioteka Refit. Używając Refit, możemy w prosty sposób deklarować interfejsy reprezentujące zasoby dostępne przez REST API, a sama biblioteka zajmuje się generowaniem odpowiednich zapytań HTTP. Refit obsługuje także zarządzanie nagłówkami, parametrami i ciałem żądania w sposób deklaratywny.
Aby rozpocząć, wystarczy dodać odpowiednią paczkę NuGet (refit) i przygotować interfejs, jak w przykładzie poniżej:
W tym przypadku, Refit automatycznie wygeneruje implementację klasy, która będzie wysyłać żądania HTTP na podstawie tego interfejsu. Dzięki temu kod staje się znacznie czystszy i łatwiejszy do utrzymania.
Podsumowanie
Zarządzanie dostępem do danych z zewnętrznych źródeł w aplikacjach ASP.NET Core jest kluczowe dla zapewnienia ich efektywności i stabilności. Stosowanie odpowiednich narzędzi, takich jak IHttpClientFactory oraz Refit, pozwala na znaczne poprawienie wydajności oraz ułatwia kodowanie, eliminując konieczność ręcznego zarządzania połączeniami HTTP.
Dla pełnej optymalizacji warto pamiętać o kilku aspektach: po pierwsze, dobrze zaplanowana konfiguracja IHttpClientFactory może pomóc w uniknięciu problemów z wyczerpywaniem gniazd sieciowych, a po drugie, użycie Refit upraszcza interakcję z API, czyniąc kod bardziej przejrzystym i łatwiejszym w utrzymaniu. Warto również dbać o właściwe zarządzanie błędami i zapewnienie odporności na awarie (resilience), zwłaszcza w przypadku interakcji z zewnętrznymi API, gdzie opóźnienia i błędy mogą być bardziej powszechne.
Jak populizm i autorytarna agitacja wpływają na współczesne społeczeństwa?
Jakie są podstawowe zasady przy wynajmowaniu sprzętu na kempingu?
Czy blockchain może istnieć bez Bitcoina?
Jak nauczyć psa przydatnych umiejętności w dzisiejszym świecie?
Jak administracja Donalda Trumpa manipulowała dokumentacją publiczną: analiza wykluczeń i ich konsekwencje dla historii
Jak osiągnąć idealną strukturę i smak lodów w domowej produkcji?
Jak rozwiązywać całki zawierające funkcje trygonometryczne i logarytmiczne?
Jakie są podstawowe kategorie produktów i jakie mają znaczenie w handlu międzynarodowym?
Jak skomponować pełnowartościową i aromatyczną miskę z indykiem i zbożami?

Deutsch
Francais
Nederlands
Svenska
Norsk
Dansk
Suomi
Espanol
Italiano
Portugues
Magyar
Polski
Cestina
Русский