Baltic-palace.com, technologia dla wyjątkowego miejsca nad Bałtykiem
Baltic Palace stoi w Pobierowie, w zachodniopomorskim pasie wydmowym, około stu metrów od zejścia na plażę. Obiekt ma czterdzieści cztery pokoje i apartamenty, więc nie jest ani pensjonatem, ani dużym hotelem sieciowym. Ta skala jest kluczowa dla zrozumienia całego projektu strony, bo decyduje o tym, jak wygląda ruch na serwisie i jak wygląda budżet na jego utrzymanie.
Obiekt zaprojektowała pracownia MTA Architekci, a bryła jest jego głównym argumentem sprzedażowym na równi z lokalizacją. Na parterze mieści się hol wejściowy, bar, restauracja, gabinety odnowy biologicznej i strefa SPA z dwoma basenami oraz saunami. Do tego dochodzi zaplecze konferencyjne. Każdy z tych elementów jest osobnym powodem, dla którego ktoś wchodzi na stronę, i każdy ma innego odbiorcę: para szukająca weekendu, rodzina planująca dwa tygodnie wakacji i koordynator szkolenia dla trzydziestu osób czytają tę samą witrynę zupełnie inaczej.
Projekt trafił do realizacji w 2017 roku. Wdrożenie zajęło około sześciu tygodni. Układ i rozmieszczenie elementów dostarczył klient, więc moją częścią było przełożenie go na szablony, model treści i zachowanie responsywne, a nie decydowanie o estetyce.
Cel baltic-palace.com i jego odbiorcy
Obiekt tej wielkości nie konkuruje z portalami rezerwacyjnymi zasięgiem. Konkuruje z nimi marżą. Każda rezerwacja przechodząca przez pośrednika zostawia u niego prowizję, więc strona własna ma jedno zadanie mierzalne w pieniądzu: przekonać gościa, który już zna nazwę obiektu, żeby dokończył rezerwację u źródła, zamiast wracać do wyszukiwarki ofert. To zmienia priorytety. Nie chodzi o pozyskanie ruchu z niczego, tylko o to, żeby ruch markowy, który i tak przychodzi, nie odpłynął z powodu wolno ładującej się galerii albo formularza, który gubi dane przy cofnięciu.
Drugi odbiorca to rynek konferencyjny i grupowy. Ten czyta stronę zupełnie inaczej, bo szuka liczb i ograniczeń: ile osób zmieści się w sali, czy da się zrobić przerwę kawową w restauracji, jak daleko jest do najbliższej stacji kolejowej. Serwis musiał więc unieść dwa równoległe tryby czytania, emocjonalny i specyfikacyjny, bez rozbijania się na dwie osobne witryny.
Trzeci wątek jest sezonowy i to on najmocniej ukształtował decyzje techniczne. Ruch na stronie obiektu wypoczynkowego nad Bałtykiem nie jest równomierny. Rozkłada się na kilka krótkich pików: początek roku, gdy planuje się lato, długie weekendy majowe, i szczyt w wakacje. Między nimi są tygodnie, w których serwis obsługuje kilkadziesiąt wejść dziennie. Infrastruktura utrzymywana pod stały peak byłaby przez większość roku pieniędzmi wyrzucanymi w powietrze, a infrastruktura skrojona pod średnią przewróciłaby się dokładnie w tygodniu, w którym obiekt zarabia najwięcej.
Osobnym wątkiem jest strefa SPA i zaplecze konferencyjne, które w strukturze serwisu zachowują się inaczej niż pokoje. Pokój ma dostępność i cenę, więc jest rekordem. Strefa SPA nie ma dostępności w tym sensie, ma za to zabiegi, godziny otwarcia i sezonowe ograniczenia. Sala konferencyjna ma pojemność w kilku układach ustawienia krzeseł i to właśnie ta liczba, a nie zdjęcie, decyduje o zapytaniu od organizatora. Próba wtłoczenia wszystkich trzech bytów w jeden typ treści kończy się polami, które w dwóch trzecich przypadków zostają puste, i panelem redakcyjnym, w którym nikt po roku nie pamięta, co gdzie wpisać.
Techniczne funkcjonalności baltic-palace.com
Frontend opiera się na Tailwind CSS i zapytaniach medialnych, z oglądem na WCAG 2.1. W obiekcie noclegowym dostępność nie jest ćwiczeniem formalnym. Znaczna część gości to osoby starsze, a strona rezerwacyjna czytana na telefonie w słońcu wymaga kontrastu, którego ładny jasnoszary tekst na białym tle po prostu nie daje. Z tego samego powodu pola formularza rezerwacyjnego mają widoczne etykiety, a nie placeholdery znikające przy pierwszym znaku.
Galerie i prezentacja bryły to najcięższa część serwisu. Zdjęcia architektury i wnętrz są tu treścią pierwszorzędną, a nie ozdobą, więc kompresja do poziomu, w którym faktura tynku zamienia się w plamę, odbiera stronie jej główny argument. Bryłę pokazuje model trójwymiarowy w Three.js, a galerie zdjęć ładują się dynamicznie przez GraphQL z optymalizacją srcset. GraphQL ma tu konkretne uzasadnienie: klasyczne API zwraca dla galerii komplet pól każdego zdjęcia, łącznie z tymi, których widok listy nie używa, a pojedyncze zapytanie po kilkanaście obiektów z pełnymi metadanymi potrafi ważyć więcej niż same miniatury. Zapytanie deklarujące wprost, że potrzebuje trzech pól, przenosi ten koszt z sieci do bazy, gdzie jest tańszy.
Rezerwacje obsługuje dedykowany moduł z integracją Stripe, walidacją po stronie serwera i zapisem w PostgreSQL z szyfrowaniem AES-256. Walidacja po stronie serwera nie jest tu nadmiarem względem walidacji w przeglądarce. Formularz rezerwacyjny przyjmuje zakres dat, a zakres dat jest polem, które najłatwiej złamać: cofnięcie przeglądarki, dwie karty otwarte równolegle, wybór terminu, który w międzyczasie został zajęty przez kogoś innego. Każdy z tych przypadków wygląda po stronie klienta jak poprawny formularz.
Sekcja informacyjna o Pobierowie i o obiekcie jest zoptymalizowana pod frazy, którymi ludzie faktycznie szukają noclegu w tej okolicy, z przyspieszonym zgłaszaniem zmian przez Google Indexing API. Kopie zapasowe idą automatycznie na Amazon S3 z replikacją między regionami, wersjonowaniem i kompresją Zstandard. Wersjonowanie jest tu ważniejsze niż sama kopia: awaria, która faktycznie zdarza się obiektom noclegowym, to nie utrata serwera, tylko nadpisanie cennika złym plikiem na dwa dni przed majówką.
Warstwa wydajnościowa to cache serwerowy w Varnish oraz optymalizacja multimediów przez Cloudflare, z WebP i preconnect dla kluczowych zasobów w HTTP/3. Lokalizację pokazuje moduł oparty o Mapbox GL JS z danymi GeoJSON i kafelkowaniem. Mapa w obiekcie nadmorskim odpowiada na pytanie, którego nie zadaje się wprost: jak daleko naprawdę jest do morza, skoro każdy obiekt w promieniu kilometra pisze o sobie, że leży przy plaży.
Sezonowość ma jeszcze jedną konsekwencję, mniej oczywistą niż wydajność. Treść oferty starzeje się skokowo. Cennik na kolejny rok, terminy turnusów, menu restauracji i zakres pakietów SPA zmieniają się w jednym momencie, zwykle jesienią, i wtedy ktoś po stronie obiektu musi zaktualizować kilkanaście miejsc naraz. Jeżeli te dane siedzą wpisane na sztywno w treści podstron, aktualizacja zawsze kończy się tym, że jedna podstrona zostaje ze starą ceną do wiosny. Dlatego model treści rozdziela to, co jest opisem, od tego, co jest parametrem, i pozwala zmienić parametr w jednym miejscu.
Wyzwania techniczne i ich rozwiązania
Obciążenie galerii było pierwszym problemem, który wyszedł na testach. Duża liczba zdjęć w wysokiej rozdzielczości opóźniała pierwsze wyświetlenie. Rozwiązaniem był Redis na cache zapytań i Fastly jako druga warstwa CDN dla równoległego serwowania multimediów. Sensem rozbicia multimediów na osobną ścieżkę jest to, że galeria i dokument HTML mają zupełnie inny profil unieważniania: tekst oferty zmienia się co kilka tygodni, a zdjęcia z sesji praktycznie nigdy, więc trzymanie ich pod tą samą polityką cache oznacza albo zbyt częste przeładowywanie obrazów, albo zbyt rzadkie odświeżanie treści.
Model trójwymiarowy obciążał przeglądarki mobilne, co przy obiekcie, którego ruch w dużej części pochodzi z telefonów, było problemem realnym, a nie teoretycznym. Redukcja liczby wielokątów i kompresja tekstur przez Draco załatwiły sprawę po stronie transferu. Warto jednak nazwać kompromis wprost: każda taka redukcja odbiera modelowi detal, a detal jest tym, po co ten model w ogóle powstał. Granica przebiega tam, gdzie bryła nadal czyta się jako ta konkretna bryła, a nie jako bryła w ogóle.
System rezerwacyjny zacinał się przy skokach ruchu. Tutaj przyczyna jest strukturalna: rezerwacja to operacja, która musi sprawdzić dostępność, zablokować termin, pobrać płatność i wysłać potwierdzenie, a użytkownik czeka na ostatni z tych kroków, choć interesuje go tylko pierwszy. Rozdzielenie ich przez RabbitMQ sprawia, że blokada terminu i odpowiedź dla użytkownika dzieją się natychmiast, a wysyłka potwierdzenia i synchronizacja z resztą systemu idą kolejką. Rate limiting na poziomie Nginx chroni ten sam endpoint przed podwójnym kliknięciem i przed botami, które w sezonie skanują dostępność terminów.
Nieaktualny cache był ostatnim z tych wyzwań i najbardziej kosztownym biznesowo. Zmiana w ofercie, której nie widać, to telefon od gościa z pretensją o cenę. Varnish z purge wyzwalanym webhookiem oraz Edge Side Includes dla sekcji dynamicznych rozwiązują to bez rezygnacji z cache dla reszty strony. ESI jest tu odpowiedzią na konkretne napięcie: strona pokoju w dziewięćdziesięciu procentach składa się z treści, która nie zmienia się miesiącami, i w kilku procentach z ceny oraz dostępności, które zmieniają się codziennie. Bez rozdzielenia tych warstw cache trzeba by ustawić według najkrótszego czasu życia w całym dokumencie, czyli praktycznie wyłączyć.
Trzeba też uczciwie zaznaczyć, że warstwa, na którą wykonawca ma najmniejszy wpływ, jest w tej branży najważniejsza. Materiał zdjęciowy decyduje o rezerwacji mocniej niż każda optymalizacja czasu ładowania. Zadaniem strony jest nie przeszkadzać temu materiałowi: nie kadrować go automatycznie tak, że urywa się bryła budynku, nie ładować go w kolejności, w której pierwsze widoczne zdjęcie jest zdjęciem korytarza, i nie degradować go kompresją do poziomu, w którym niebo nad wydmą rozpada się na pasy.
Zastosowane technologie
Yoast SEO odpowiada za metadane, mapy witryn XML i powiadamianie wyszukiwarek o aktualizacjach. UpdraftPlus obsługuje kopie na Amazon S3 z replikacją między regionami i szyfrowaniem AES-256. Cloudflare pełni rolę CDN z Argo Smart Routing, kompresją Brotli i ochroną przed ruchem wolumetrycznym. Redis trzyma cache obiektowy z shardingiem i trwałym zapisem dla zapytań oraz sesji. Varnish realizuje cache stron na własnym VCL, z trybem grace i obsługą ESI.
Tryb grace zasługuje na osobne zdanie, bo jest najbardziej niedocenianym ustawieniem w tej konfiguracji. Pozwala oddać użytkownikowi lekko przeterminowaną wersję strony, kiedy backend nie odpowiada, zamiast pokazać błąd. Dla obiektu noclegowego różnica między stroną sprzed dwóch minut a komunikatem o awarii w szczycie sezonu to różnica między rezerwacją a jej brakiem.
Lighthouse biegnie automatycznie w procesie CI/CD na GitHub Actions, więc regresja wydajnościowa wychodzi przy zmianie, a nie przy skardze. RabbitMQ kolejkuje zadania rezerwacyjne i wysyłkę potwierdzeń z ponawianiem. Fastly dokłada drugą ścieżkę dystrybucji multimediów z optymalizacją geograficzną. Mapbox GL JS rysuje mapę z kafelkowaniem. GraphQL obsługuje ładowanie danych galerii i sekcji informacyjnych z batchingiem zapytań, a Git wraz z całym potokiem CI utrzymuje historię zmian w stanie, w którym da się cofnąć jedną rzecz, a nie cały tydzień pracy.
Warto dodać, o czym ta lista nie mówi. Nie mówi o tym, jak zachowa się identyczny stos na innym obiekcie. Trzy poziomy cache przy czterdziestu czterech pokojach i silnej sezonowości są uzasadnione dokładnie tym kształtem ruchu. Przy obiekcie działającym równo przez cały rok ta sama konfiguracja byłaby nadmiarem, którego koszt utrzymania przewyższyłby zysk.
Warto też powiedzieć, czego na tej stronie nie ma i dlaczego. Nie ma tu wskaźników wyniku: procentu wzrostu rezerwacji, oceny w skali pięciu punktów ani liczby odwiedzin po wdrożeniu. Dane sprzedażowe obiektu należą do obiektu, a nie do studium przypadku wykonawcy, i żadnej z tych liczb nie dałoby się tu potwierdzić źródłem. To, co da się opisać uczciwie i co faktycznie przenosi się na kolejne wdrożenie, to decyzje techniczne i powody, dla których zapadły.
Jest jeszcze jedno napięcie, które w obiektach noclegowych wraca zawsze i które warto nazwać przed startem projektu, a nie po nim. Strona własna i portal rezerwacyjny mówią o tym samym pokoju dwoma różnymi językami. Portal ma wymuszony format opisu, sztywny zestaw udogodnień i własne zasady kadrowania zdjęć, więc wygląda jednakowo dla każdego obiektu w mieście. Strona własna jest jedynym miejscem, w którym da się pokazać to, czego formularz portalu nie obsługuje: układ tarasu, widok z konkretnego piętra, sposób, w jaki światło wchodzi do sali restauracyjnej po południu. Jeżeli strona własna kopiuje strukturę portalu, traci swój jedyny atut i zostaje z gorszą wyszukiwarką terminów.
Z perspektywy utrzymania najwięcej kosztuje nie kod, tylko rozjazd między tym, co jest na stronie, a tym, co mówi recepcja. Każda integracja, która ten rozjazd zmniejsza, zwraca się szybciej niż kolejna warstwa cache, i to jest zwykle właściwy kierunek rozbudowy takiego serwisu po pierwszym sezonie.
Zarządzanie i wsparcie techniczne
Serwis wymaga stałego monitoringu. Aktualizacje systemu i wtyczek przechodzą przez środowisko testowe z pełną kopią, a nie przez produkcję z nadzieją. Różnica między jednym a drugim jest w obiekcie noclegowym najbardziej widoczna właśnie w module rezerwacyjnym: aktualizacja testowana na pustej instalacji przechodzi zawsze, bo nie ma czego zepsuć, a ta sama aktualizacja na kopii produkcji od razu pokazuje, że wtyczka zmieniła format daty w istniejących rekordach.
Cloudflare, Redis i Fastly odpowiadają za wydajność przy wzmożonym ruchu, Varnish i RabbitMQ za stabilność procesów dynamicznych. Zapytania SQL idą przez indeksy złożone, bo wyszukiwanie dostępności zawsze filtruje po dacie i typie pokoju jednocześnie, a indeks założony na jedną z tych kolumn osobno nie pomaga w takim zapytaniu. Pamięć podręczna jest czyszczona punktowo przy aktualizacjach treści, nie hurtowo, bo pełny purge przed weekendem majowym oznacza, że pierwsze kilkaset odsłon trafia na zimny cache.
Serwis da się rozbudować o integrację z systemem hotelowym po stronie recepcji, moduł promocji sezonowych albo sekcję opinii gości. Każdy z tych kierunków jest osobnym zakresem, wycenianym po analizie, a nie dopisywanym po drodze.
Planujesz witrynę dla swojego obiektu noclegowego? Szukasz profesjonalnej platformy z zaawansowanym wsparciem technicznym? Jako specjalista WordPress pomagam w realizacji złożonych projektów. Sprawdź cennik usług WordPress lub napisz z założeniami projektu i opisz cel strony, ofertę oraz ograniczenia techniczne.
Często zadawane pytania
Najważniejsze odpowiedzi, które pomagają wdrożyć temat w praktyce.
Jakiego zakresu dotyczył projekt BALTIC PALACE?
#Jak wyglądał przebieg prac przy BALTIC PALACE?
#Co było najtrudniejsze technicznie w BALTIC PALACE?
#Co z projektu BALTIC PALACE da się wykorzystać przy kolejnym wdrożeniu?
#Potrzebujesz FAQ dopasowanego do branży i rynku? Przygotujemy wersję pod Twoje cele biznesowe.
Porozmawiajmy