Cześć, Habr!
W tym artykule rozpoczniemy opowieść o tym, jak działa wewnętrznie i jakie technologie są używane w jej rozwoju.

Dlaczego uważamy, że to jest interesujące? Po pierwsze, ponieważ platforma «1C:Enterprise 8» to duża (ponad 10 milionów linijek kodu) aplikacja napisana w C++ (klient, serwer itd.), JavaScript (klient webowy), a od niedawna także . Duże projekty są interesujące chociażby ze względu na ich skalę, ponieważ problemy, które w małej bazie kodu są niewidoczne, w takich projektach stają się wyraźnie widoczne. Po drugie, «1C:Enterprise» to replikowany, "pudełkowy" produkt, a artykułów o takich rozwiązaniach na Habrze jest bardzo mało. Zawsze interesuje również, jak to wygląda w innych zespołach i firmach.
Zatem zaczynamy. W tym artykule przedstawimy przegląd niektórych technologii stosowanych w platformie, nakreślimy krajobraz, bez głębokiego zagłębiania się w implementację. Wiele mechanizmów wymagałoby szczegółowego opisu na osobny artykuł, a niektóre — na całą książkę!
Na początek warto określić podstawowe rzeczy — czym jest platforma «1C:Enterprise» i z jakich komponentów się składa. Odpowiedź na to pytanie nie jest prosta, ponieważ termin „Platforma” (dla uproszczenia będziemy ją tak nazywać) obejmuje zarówno narzędzie do tworzenia aplikacji biznesowych, jak i środowisko wykonawcze oraz narzędzia administracyjne. Można w przybliżeniu wyróżnić następujące składniki:
- klaster serwerów
- „cienki” klient, mogący łączyć się z serwerem przez HTTP oraz własny protokół binarny
- klient do pracy w architekturze dwuwarstwowej z bazą danych, umieszczoną na dysku twardym lub w folderze sieciowym
- klient webowy
- narzędzia administracyjne dla serwera aplikacji
- środowisko programistyczne (znane jako Konfigurator)
- środowisko wykonawcze dla iOS, Android i Windows Phone (mobilna platforma 1C)
Wszystkie te części, z wyjątkiem klienta webowego, są napisane w C++. Ponadto, niedawno ogłoszono , napisany w Javie.
Aplikacje natywne
Do rozwijania aplikacji natywnych używa się C++03. W systemie Windows jako kompilatora używa się Microsoft Visual C++ 12 (profil zgodny z Windows XP), a w systemach Linux i Android — gcc 4.8, dla iOS — clang 5.0. Standardowa biblioteka jest wspólna dla wszystkich systemów operacyjnych i kompilatorów — STLPort. To rozwiązanie pozwala zredukować ryzyko błędów specyficznych dla implementacji STL. W tej chwili planujemy przejście na implementację STL dostarczaną z CLang, ponieważ STLPort przestał być rozwijany i nie jest zgodny z włączonym trybem wsparcia C++11 w gcc.
Baza kodu serwera jest przy tym wspólna w 99%, a klienta — w 95%. Co więcej, nawet platforma mobilna używa tego samego kodu C++, co „duża”, chociaż tam stopień unifikacji jest nieco niższy.
Jak większość użytkowników C++, nie korzystamy z 100% możliwości języka i jego bibliotek. Tak, w praktyce klient nie używa Boost, a z możliwości języka — dynamicznego rzutowania typów. Przy tym aktywnie stosujemy:
- STL (w szczególności łańcuchy, kontenery i algorytmy)
- dziedziczenie wielokrotne, w tym dziedziczenie implementacji
- szablony
- wyjątki
- inteligentne wskaźniki (własna implementacja)
Dzięki stosowaniu wielokrotnego dziedziczenia interfejsów (całkowicie abstrakcyjnych klas) staje się możliwy model komponentowy, o którym mowa będzie poniżej.
Składniki
Aby zapewnić modularność, cała funkcjonalność została podzielona na komponenty, które stanowią dynamiczne biblioteki (*.dll w systemie Windows, *.so — w systemie Linux). Łącznie jest ponad półtora setki komponentów, oto opisy niektórych z nich:
backend
Zawiera «silnik» metadanych platformy
accnt
Obiekty, które programiści aplikacji używają do tworzenia księgowości (plany kont i rejestry księgowe)
bsl
Silnik wykonawczy wbudowanego języka
nuke
Własna implementacja alokatora pamięci
dbeng8
Silnik bazy plików. Prosta maszyna baz danych typu plik-serwer oparta na ISAM, obejmująca również prosty procesor SQL
wbase
Zawiera podstawowe klasy i funkcje do realizacji interfejsu użytkownika w systemie Windows — klasy okien, dostęp do GDI itd.
Podział na wiele komponentów jest korzystny z kilku punktów widzenia:
- Podział sprzyja lepszemu projektowaniu, a w szczególności lepszej izolacji kodu
- Z zestawu komponentów można elastycznie konfigurować różne opcje dostawy:
- Na przykład instalacja cienkiego klienta będzie zawierać wbase, ale nie będzie zawierać backendu.
- Z kolei na serwerze wbase nie będzie.
- Oba warianty będą oczywiście zawierały nuke i bsl.
Wszystkie komponenty potrzebne do uruchomienia tej wersji są ładowane przy starcie programu. To jest szczególnie ważne dla rejestracji klas SCOM, o których będzie mowa poniżej.
SCOM
Do dekompozycji na niższym poziomie używana jest system SCOM — ideologicznie podobny do biblioteki ATL. Dla tych, którzy nie pracowali z ATL, krótko wymienię główne możliwości i cechy.
Dla specjalnie sformatowanej klasy SCOM:
- Oferuje metody fabryczne, które pozwalają stworzyć klasę z innego komponentu, znając tylko jego nazwę (bez odkrywania implementacji).
- Oferuje infrastrukturę inteligentnych wskaźników z liczeniem odniesień. Nie trzeba ręcznie monitorować czasu życia klasy SCOM.
- Pozwala ustalić, czy obiekt implementuje dany interfejs i automatycznie przekształca wskaźnik na obiekt w wskaźnik na interfejs.
- Umożliwia stworzenie obiektu-usługi, zawsze dostępnego przez metodę get_service itd.
Na przykład można opisać w komponencie json.dll klasę do odczytu JSON (np. JSONStreamReader).
Klasy, instancje można tworzyć z innych komponentów, które muszą być zarejestrowane w maszynie SCOM:
SCOM_CLASS_ENTRY(JSONStreamReader)
Ten makro opisze specjalną statyczną klasę-rejestrator, której konstruktor zostanie wywołany przy ładowaniu komponentu do pamięci.
Po tym można stworzyć jej instancję w innym komponencie:
IJSONStreamReaderPtr jsonReader = create_instance(SCOM_CLSIDOF(JSONStreamReader));
Aby wspierać usługi, SCOM oferuje dodatkową, dość złożoną infrastrukturę. Centralnym pojęciem jest proces SCOM, który służy jako kontener dla uruchomionych usług (tzn. pełni rolę Service Locator) oraz zawiera powiązania z lokalizowanymi zasobami. Proces SCOM jest powiązany z wątkiem OS. Dzięki temu wewnątrz aplikacji można w ten sposób uzyskiwać usługi:
SCOM_Process* process = core::current_process();
if (process)
return get_service(process);
Co więcej, przełączając procesy logiczne (SCOM) związane z wątkiem, można uzyskać praktycznie niezależne, z punktu widzenia przestrzeni informacyjnej, aplikacje działające w ramach jednego wątku. Tak działa nasz cienki klient, współpracujący z bazą plików — w jednym procesie systemu operacyjnego znajdują się dwa procesy SCOM, jeden związany z klientem, a drugi z serwerem. Takie podejście pozwala na ujednolicenie pisania kodu, który będzie działał zarówno na lokalnej bazie plików, jak i w „prawdziwym” wariancie klient-serwer. Cena za taką jednolitość to koszty dodatkowe, ale praktyka pokazuje, że są tego warte.
Na podstawie komponentowej modelu SCOM zrealizowano zarówno logikę biznesową, jak i część interfejsu 1C: Enterprise.
Interfejs użytkownika
A propos interfejsów. Nie używamy standardowych kontrolek Windows, nasze elementy sterujące są zrealizowane bezpośrednio na Windows API. Dla wersji Linux została stworzona warstwa pośrednia, działająca poprzez bibliotekę wxWidgets.
Biblioteka elementów sterujących nie zależy od innych części „1C: Enterprise” i jest używana przez nas także w kilku niewielkich wewnętrznych narzędziach.
Na przestrzeni lat rozwoju 1C: Enterprise wygląd kontrolek zmieniał się, ale poważne zmiany zasad miały miejsce tylko raz, w 2009 roku, wraz z wydaniem wersji 8.2 i pojawieniem się „sterowanych formularzy”. Oprócz zmiany wyglądu fundamentalnie zmieniła się zasada kompozycji formularzy — zrezygnowano z pozycji elementów piksel po pikselu na rzecz kompozycji elementów w stylu flow. Ponadto w nowym modelu elementy sterujące nie działają bezpośrednio z obiektami domeny, lecz ze specjalnymi DTO ().
Te zmiany pozwoliły stworzyć klienta webowego „1C: Enterprise”, który odzwierciedla logikę kontrolek C++ w JavaScript. Staramy się utrzymać funkcjonalną równoważność między cienkim a webowym klientem. W przypadku, gdy to niemożliwe, na przykład z powodu ograniczeń dostępnych z JavaScript API (na przykład możliwości pracy z plikami są bardzo ograniczone), często realizujemy potrzebną funkcjonalność za pomocą rozszerzeń przeglądarek, napisanych w C++. Obecnie wspieramy Internet Explorer i Microsoft Edge (Windows), Google Chrome (Windows), Firefox (Windows i Linux) oraz Safari (MacOS).
Ponadto technologia zarządzanych formularzy jest wykorzystywana do tworzenia interfejsu aplikacji mobilnych na platformie 1C. Na urządzeniach mobilnych renderowanie kontrolek jest realizowane z użyciem technologii „rodzimych” dla systemu operacyjnego, ale logika układu formularza i reakcje interfejsu korzystają z tego samego kodu, co w „dużej” platformie „1C:Przedsiębiorstwo”.

Interfejs 1C na systemie Linux

Interfejs 1C na urządzeniu mobilnym
Interfejs 1C na innych platformach 
Interfejs 1C na systemie Windows

Interfejs 1C — klient internetowy
Open source
Chociaż nie używamy standardowych bibliotek C++ dla systemu Windows (MFC, kontrolki z WinAPI), nie wszystkie komponenty piszemy sami. Już wspomniana biblioteka , a ponadto używamy:
- do pracy z HTTP i FTP.
- do pracy z kryptografią i nawiązywania połączeń TLS
- do analizy XML
- do pracy z protokołami pocztowymi (POP3, SMTP, IMAP)
- do analizy wiadomości e-mail
- do przechowywania dzienników aktywności użytkowników
- do internacjonalizacji
Listę można by kontynuować.
Ponadto używamy mocno zmodyfikowanej wersji i przy tworzeniu testów jednostkowych.
Biblioteki wymagały dostosowania do zgodności z modelem SCOM organizacji komponentów.
Powszechność 1C sprawia, że platforma jest doskonałym testem wytrzymałości dla wykorzystywanych w niej bibliotek. Różnorodność użytkowników i scenariuszy szybko ujawnia błędy nawet w najrzadziej używanych fragmentach kodu. Naprawiamy je u siebie i staramy się przekazać je z powrotem autorom bibliotek. Doświadczenie interakcji bywa bardzo różne.
Deweloperzy i szybko reagują na pull-request, ale łatka, na przykład, w nigdy nie udało się nam oddać.
Podsumowanie
W artykule poruszyliśmy kilka podstawowych aspektów rozwoju platformy „1C: Przedsiębiorstwo”. W ograniczonym zakresie artykułu dotknęliśmy tylko niektórych interesujących, naszym zdaniem, aspektów.
Ogólny opis różnych mechanizmów platformy można zobaczyć .
Jakie tematy byłyby dla Ciebie interesujące w kolejnych artykułach?
Jak zrealizowano mobilną platformę 1C?
Opis wewnętrznej struktury klienta internetowego?
A może interesuje Cię proces wyboru funkcji do nowych wydań, tworzenia i testowania?
Napisz w komentarzach!
Źródło: habr.com
