
Wiadomość w czacie ma bardzo mały budżet cierpliwości. Jeśli odwiedzający wysyła pytanie i przez kilka sekund nic się nie dzieje, produkt zaczyna wyglądać podejrzanie. Może wiadomość nie doszła. Może nikogo nie ma po drugiej stronie. Może widget jest tylko formularzem kontaktowym z ładniejszym przyciskiem. To wrażenie ma znaczenie, dlatego dostarczanie wiadomości traktujemy jako część doświadczenia produktu, a nie detal schowany za API.
Główna ścieżka w Convorze jest celowo prosta. Widget albo panel wysyła żądanie do API Fastify. Serwer sprawdza użytkownika, organizację, czyści treść wiadomości, zapisuje ją w PostgreSQL i dopiero wtedy publikuje zdarzenie do warstwy realtime. Kolejność jest ważna. Źródłem prawdy jest baza danych. WebSocket przyspiesza aktualizację, ale nie staje się miejscem, w którym mieszka rozmowa.
Dlaczego odpytywanie nie wystarcza
Odpytywanie kusi, bo łatwo je zbudować. Co kilka sekund przeglądarka pyta: czy jest coś nowego? To bywa wystarczające dla prostego licznika powiadomień. Dla rozmowy jest słabe. Krótkie interwały marnują żądania. Długie sprawiają, że odpowiedzi wyglądają na spóźnione. Najgorsza jest nierówność. Jedna karta złapie wiadomość prawie od razu, druga poczeka do następnego okna odpytywania. Czat wsparcia nie powinien zależeć od takiego przypadku.
Convor używa Centrifugo do stałych połączeń. Operatorzy subskrybują kanały organizacji i rozmów. Odwiedzający subskrybuje wyłącznie kanał swojej rozmowy. Gdy API publikuje zdarzenie wiadomości, Centrifugo wysyła je do klientów, którzy mogą je odebrać. Panel nie musi odkrywać zmiany przez ciągłe pytanie serwera. Dostaje ją wtedy, gdy serwer zapisze wiadomość.
Praca wokół wiadomości
Wysłanie wiadomości uruchamia też inną pracę. Zmieniają się liczniki nieprzeczytanych wiadomości. Mogą odpalić się reguły automatyzacji. Webhook wychodzący może trafić do kolejki. Analiza sentymentu albo tłumaczenie może dostać osobne zadanie. Operator może dostać powiadomienie push. Żadna z tych rzeczy nie powinna opóźniać pokazania odpowiedzi w czacie. Ścieżka wiadomości zostaje krótka, a wolniejsza praca idzie przez kolejki albo osobne serwisy.
Informacja o pisaniu idzie lżejszą ścieżką, bo jest tymczasowa. Sygnał, że ktoś pisze, jest przydatny przez kilka sekund, a później nie ma wartości. Potwierdzenia odczytu są bliżej zapisanego stanu, bo wpływają na liczniki i to, co widzi operator. Trzymamy te pojęcia osobno. Tabela wiadomości powinna przechowywać wiadomości, a nie każdy drobny sygnał UI z rozmowy.
Słaba sieć to normalny przypadek
Ścieżkę idealną łatwo pokazać w demo. Prawdziwi użytkownicy zmieniają sieć, zamykają laptop, wracają do karty i siedzą za firmowymi firewallami, które ubijają bezczynne połączenia. Widget musi zakładać, że WebSocket może zniknąć. Gdy tak się stanie, zachowuje tożsamość rozmowy, łączy się ponownie i w razie potrzeby doczytuje historię z API. Utracone połączenie nie może oznaczać utraconej rozmowy. W najgorszym razie klient nadrabia z PostgreSQL i działa dalej.
Dlatego autoryzacja kanałów ma znaczenie. Odwiedzający nie może subskrybować cudzej rozmowy. Operator nie może dostawać zdarzeń z innej organizacji. UI powinien tego pilnować, ale broker również. Systemy realtime są szybkie, a szybkie pomyłki rozchodzą się szeroko, jeśli granice są luźne.
W praktyce podział jest prosty: Fastify odpowiada za walidację i zapis, PostgreSQL trzyma historię, Centrifugo dostarcza zdarzenia do podłączonych klientów, a Redis z BullMQ przejmują pracę, której nie powinno być przed odwiedzającym. Taką architekturę można rozwijać bez dokładania każdej nowej funkcji do krytycznej ścieżki wysyłania wiadomości.
Nowe wpisy na Twoją skrzynkę
Bez spamu. Wypisz się w dowolnej chwili.

