Wyślij zapytanie Dołącz do Sii

Przejrzysta i zrozumiała architektura to jeden z kluczy do sukcesu w tworzeniu oprogramowania[1]. Architekci i deweloperzy podejmują dziesiątki decyzji architektonicznych dziennie. W skomplikowanym środowisku, w którym miesza się wiele technologii, takie działania pochłaniają siły umysłowe pracowników. Adobe Experience Manager bez wątpienia należy do kategorii „skomplikowane”.

W tym artykule przedstawię koncepcję modułów i granic pomiędzy nimi, dzięki czemu w dłuższej perspektywie dostarczanie dopasowanego systemu komponentów w ramach AEM jest mniej stresujące i łatwiejsze do zmiany.

Doświadczenie Sii w dostarczaniu systemów zarządzania treścią opartych na Adobe Experience Manager nauczyło nas myślenia o stworzonym oprogramowaniu jako o zbiorze ruchomych części, które mogą się ze sobą łączyć i konstruować rozwiązanie. Poniżej przedstawię Ci ten sposób myślenia: mistycyzm projektowania dobrej architektury bez zbytniego obciążenia umysłu zbędnymi decyzjami.

Ten wpis nie jest przeznaczony wyłącznie dla specjalistów AEM, ale także dla osób, które projektują większe rozwiązania, w których AEM odgrywa rolę – osób z rolą solution architect i podobnych.

Krótko o Adobe Experience Manager

AEM to najlepsze rozwiązanie CMS na rynku, o ogromnej liczbie funkcji i zastosowań, obecnie dostarczane jako usługa w chmurze. W przeszłości firma Adobe udostępniała AEM jako oprogramowanie uruchamiane na serwerach właściciela licencji, a kilka lat później klienci zainteresowali się rozwiązaniem hybrydowym – Adobe Managed Services – w którym firma Adobe hostowała aplikację we własnej chmurze.

Można by pomyśleć, że te drastyczne zmiany w metodach wdrażania poważnie wpłynęły na rzeczywistą architekturę wewnętrzną AEM. Tak się jednak nie stało – Adobe postawiło sobie za cel utrzymanie jej w tej samej formie. W związku z tym, w artykule wspominam o elementach, które istnieją od wyjątkowo dawna, więc nie musisz sprawdzać, z którą wersją aktualnie pracujesz.

AEM w oczach specjalistów

Solution architect powinien myśleć o AEM jako o serwerze WWW. Ogólnie rzecz biorąc, AEM zapewnia interfejs użytkownika do tworzenia treści, które serwer udostępnia jako HTML/JSON/XML/*.

Architekt odpowiedzialny za implementację lub programista powinni myśleć o nim, jako o zestawie komponentów, którymi można uformować stronę z kawałków o oczekiwanym formacie, pamiętając o poniższych istotnych faktach dotyczących komponentów:

  • Content authors lub inne bezosobowe podmioty, takie jak systemy zewnętrzne, wypełniają komponenty danymi – dzięki czemu możemy traktować komponent jako typ informacji, a komponent jako treść określonego typu.
  • Komponenty mogą mieć wewnątrz inne komponenty, wliczając w to siebie (ten sam typ komponentu).
  • Komponent oddziałuje z innymi obiektami poza światem komponentów – wyjaśnię to niżej:
Komponenty zagnieżdżone w celu renderowania strony
Ryc. 1 Komponenty zagnieżdżone w celu renderowania strony

Struktura przykładowej strony

Powyższy diagram przedstawia strukturę przykładowej strony. Jest to oczywiście bardzo uproszczony, ale ważny widok na całą architekturę zastosowaną w AEM. Zagnieżdżanie w komponentach może być wymuszone (tak, aby komponent w środku był „podany” przez rodzica) lub swobodne (to jest decyzja autora treści, aby zagnieździć ten lub inny komponent).

W ten sposób generuje się HTML strony. Główny servlet domyślny wywołuje na najwyższym komponencie akcję wyrenderowania siebie, a wywołany proces komponentu dodatkowo wywoła zagnieżdżone komponenty, jeżeli takie istnieją.

Aby zweryfikować rozumienie tego konceptu, spróbujmy coś zepsuć. Jeśli utworzysz komponent A, który zagnieżdża B, a B zagnieżdża się z powrotem A, skończy się to nieskończoną pętlą. Nie martw się o śmierć JVM-a – implementacja ma maksymalną liczbę komponentów wywoływanych w ramach żądania. Niemniej, jest to ciekawy eksperyment myślowy, który sprawdzi zrozumienie tego mechanizmu.

Sposób wprowadzenia pętli nieskończonej za pomocą metody zagnieżdżania komponentów
Ryc. 2 Sposób wprowadzenia pętli nieskończonej za pomocą metody zagnieżdżania komponentów

Komponenty

Ta wiedza jest kluczowa do przetrawienia następnej sekcji – rozłożymy komponent na czynniki pierwsze, dzieląc pod względem użytej technologii.

Jeśli spróbujesz wyobrazić sobie swoją pierwszą stronę hello world zaprojektowaną przy użyciu komponentów – prawdopodobnie jest to komponent strony głównej, który będzie renderował niezbędne tagi HTML i tekstowy, który wyświetla tekst.

Czy tak rzeczywiście powinno być?

Cóż, do pewnego stopnia – tu mówimy o największej zalecie AEM – ponieważ Adobe Experience Manager zawiera naprawdę solidny zestaw gotowych do użycia komponentów, zwanych Core Components[2]. Z mojego doświadczenia klienci implementują statyczne treści tymi gotowymi do użycia komponentami, z niewielkim dostosowaniem pod ich potrzeby i dopisaniem CSS. Nie brzmi to jak wyzwanie dla architektów czy deweloperów, prawda?

Prawdziwa zabawa zaczyna się, gdy musimy zapewnić naprawdę unikalne zachowanie na stronie – takie jak treść wynikająca z innych treści umieszczonych poza stroną, bardziej złożone przetwarzanie danych utworzonych przez content authors, zmiana informacji w czasie lub korzystanie z innych źródeł danych poza AEM. Te ostatnie nazywamy integracjami (ang. external system integration).

Rzućmy okiem na pojęcie modułów komponentów w AEM – rozłóżmy komponent na części, które możemy zrozumieć, podzielić pracę w zespole i, co ważniejsze, zaprojektować – nie wchodząc w koszty implementacji, przynajmniej na jakiś czas.

Moduły komponentu w AEM

Typy modułów w programie Adobe Experience Manager i ich zależności
Ryc. 3 Typy modułów w programie Adobe Experience Manager i ich zależności

Światy Java, JavaScript i AEM

Ten diagram może wydawać się zbyt skomplikowany, zatem przeanalizujmy go po kolei, zaczynając od poszczególnych światów (worlds):

  • Java – zawiera dowolne oprogramowanie w języku programowania Java. To przestrzeń, w której zwykle rządzi back-end deweloper.
  • JavaScript – podobna przestrzeń co Java, tylko przy użyciu JavaScript, z tą różnicą, że może być zarówno po stronie serwera, jak i po stronie przeglądarki. Osoby zajmujące się tym światem nazywane są zazwyczaj front-end deweloperami.
  • AEM World — ziemia niczyja, niespotykana poza AEM. Zazwyczaj jest to zestaw komponentów lub ich okien dialogowych (które również są komponentami!), które mogą być obsługiwane jako konfiguracje XML, pliki HTL oraz podstawowa jednostka wdrożeniowa – pakiet. Zasadniczo zajmują się nimi zarówno back-end jak i front-end deweloperzy, w zależności od przekazanego im zadania.

Ta separacja światów pomaga architektowi zrozumieć podział pracy pomiędzy inżynierów specjalizujących się w różnych dziedzinach. Rzadko zdarza się, aby jeden inżynier był w stanie profesjonalnie opanować wszystkie te trzy światy równocześnie. Architekt musi więc zebrać drużynę, aby wyruszyć w tę podróż.

Komunikacja między światami

Prześledźmy, jak światy komunikują się ze sobą:

  • Java Use lub JS Use w skryptach – w ten sposób możemy uzyskać dostęp do światów Java/JavaScript z AEM. Skrypt może wywoływać metody obiektów lub funkcje modułów do renderowania odpowiedzi.
  • Dołączenie biblioteki klienta (z AEM do JavaScript) do skryptu w celu dołączenia łącza do zasobu CSS lub JS dla przeglądarki.
  • Wywołanie serwisu OSGi z JavaScript Use – JavaScript do Java

Innymi słowy, obiekty różnych światów komunikują się poprzez identyfikatory i wywoływanie funkcji/metod na dostępnych abstrakcjach.

Na marginesie: istnieją inne sposoby komunikowania się między światami (takie jak dostęp do danych za pośrednictwem obiektów Session lub ResourceResolver i ich przechowywanie), jednak nie są one częścią zwykłego renderowania, dlatego nie są zalecane ani używane w rozwiązywaniu problemów zwykle obecnych w AEM.

Komponent AEM

Przyjrzyjmy się teraz najważniejszej abstrakcji w AEM – komponentowi AEM. Wyróżniają go następujące cechy:

  • Zagnieżdżanie. Komponent może zagnieżdżać inny komponent.
  • Tworzenie skryptów. Komponent może wskazać, jaki rodzaj skryptu renderuje dane wyjściowe (zwykle HTML). Istnieją inne opcje skryptów, takie jak JSP, jednak nie zaleca się ich używania – są przestarzałą metodą skryptową.
  • Konfigurowanie. Składnik ma swoje okno dialogowe, które umożliwia autorom treści wprowadzanie danych (takich jak tekst lub inne informacje, które mają wpływ na renderowanie składnika.

Wspomniałem w powyższych akapitach, jak ważna jest rola komponentu. Jeśli uznamy komponent za korzeń pewnego zestawu komponentów, otrzymamy strukturę drzewa, która dobrze współgra z HTML i podobnymi strukturami danych. Zagnieżdżanie jest funkcją, która jest całkowicie kontrolowana przez sam komponent. Istnieje dodatkowe rozszerzenie: komponent o nazwie „parsys” (system akapitów), ale generalnie architekt powinien wiedzieć, że jest sposób na wskazanie miejsca, w który można umieścić inne komponenty na etapie tworzenia treści.

Tą samą funkcję architekt może zaprojektować jako komponent zagnieżdżający lub kompletnie samodzielny, tj. bez zagnieżdżania innych komponentów. Ciężko jest ocenić użyteczność struktury, nie uwzględniając kontekstu i standardu domeny klienta.

Nie wyobrażam sobie komponentu galerii bez wewnętrznych komponentów reprezentujących poszczególne kafelki, choć i takie implementacje mają czasem zastosowanie. Z drugiej strony, komponent tekstowy nie powinien mieć zagnieżdżonego innego komponentu, chyba, że nie interesuje nas poniesienie dodatkowych kosztów utrzymania takiego rozwiązania.

AEM Packages

Dosłownie AEM packages (paczki AEM-owe) są plikami ZIP, które zawierają dane JCR, ale z naszej perspektywy architektonicznej pakiet zawiera zestaw komponentów, które można złożyć w jeden pakiet instalacyjny. Paczki mają swoje wersje i nazwy, co pozwala na prostą aktualizację podczas wdrażania, tj. zastąpienie tych samych komponentów innymi wersjami.

W naszym przypadku paczka instaluje przygotowane komponenty w środowisku AEM.

Skrypt

Skrypt jest zwykle kodem HTL (jak wspomniałem wcześniej: rzadziej JSP), który przekształca się w odpowiedź HTML. Wykonywanie skryptu jest oparte na webowym request-response, co oznacza, że może mieć warunki specyficzne dla kontekstu przetwarzania zapytania.

Rozważmy ten przykład:

<sly data-sly-use.related="com.sii.RelatedPages">
${properties.name}
    <ul data-sly-list.page="${related.list}">
        <li><a href="${related.path}">${page.name}</a></li>
    </ul>
</sly>

Wygląda to jak zwykły fragment kodu HTML z dodatkowym sly tagiem i dodatkowymi atrybutami data-sly-* . Te dodatkowe tagi przetwarzane są na pozomie backendu i wykonują dodatkowe działania na HTML. W powyższym przykładzie otrzymujemy Sling Model lub Java Use (w tym momencie tego nie wiemy) i otrzymujemy z niego listę za pomocą metody list i wyświetlamy każdy link do strony z jego nazwą na liście nieuporządkowanej. Properties jest obiektem domyślnym zawierający zapisane informacje, które content author wprowadza bezpośrednio w instancji komponentu.

Jest to podstawowa koncepcja wykorzystywania skryptów w AEM – wchodź w interakcję z innymi światami, definiuj warunki renderowania i uruchamiaj je.

Sling Model / Java Use / JavaScript Use

Te trzy rodzaje obiektów opierają się na różnych technologiach, ale realizują ten sam przypadek użycia. Są one przeznaczone do interakcji innych światów w skryptach. Głównym problemem architekta jest rozwiązanie dylematu odpowiedzialności: czy te obiekty powinny być wyłącznie pośrednikami do innych światów, czy same powinny przetwarzać dane i realizować logikę komponentów. W innym przypadku te zadania można przekazać bardziej ogólnym modułom JS lub klasom Java.

Zazwyczaj projektanci systemów wykorzystują fakt, że mogą zaimplementować większość funkcji przetwarzania danych w tych obiektach i są one ściśle związane z komponentami, które ich używają. Z drugiej strony przydatne jest, gdy Twoje Sling Models, klasy Java Use i moduły JS Use są współdzielone przez wiele komponentów, o ile ma to sens, i mogą być tylko pośrednikami dla bardziej skomplikowanego kodu napisanego w kodzie niezależnym od AEM. Wtedy możesz używać takiego kodu „wolnego od frameworków” w wielu systemach przygotowywanego rozwiązania.

Klasy Sling Models i Java Use to w zasadzie ta sama koncepcja, przy czym Java Use jest starszym bratem tego pierwszego. W dzisiejszych czasach programiści tworzą więcej Sling Models, jednak z pewnością znajdziesz zastane klasy Java Use w systemach o długim terminie przydatności i weźmiesz je pod uwagę w swojej architekturze.

Inną podstawową funkcjonalnością z naszego architektonicznego punktu widzenia jest to, że obiekty te mogą komunikować się z serwisami OSGi, które to nie mają bezpośredniego związku z żadnym komponentem. Jest to punkt, w którym architekci mogą przenieść odpowiedzialność z komponentów i, co ważniejsze, prowadzić interakcje z systemami spoza JVM-a AEM.

Serwis OSGi

W telegraficznym skrócie, OSGi jest sposobem na uruchamianie odseparowanych od siebie modułów ładujących klasy w Java Runtime Environment[3]. AEM, a w zasadzie jego główny framework – Sling, korzysta z tego rozwiązania, ale sam OSGi jest samodzielnym bytem, używanym w innych programach opartych na JVM, np. Eclipse IDE. Architekci uważają, że OSGi jest szczególnie interesujący ze względu na dodatkową izolację między uruchomionymi bundles – paczkami, które mogą zawierać różne klasy z tym samym qualified name, co zwykle bez dodatkowej, wątpliwej zabawy z class loaders jest niemożliwe.

Opisywanie funkcjonalności OSGi to w zasadzie temat na inny wpis, jeśli nie całą książkę, dlatego polecam zajrzeć do oficjalnej dokumentacji[4], by dowiedzieć się więcej.

Z naszego punktu widzenia pakiety OSGi są prawie jak pakiety AEM – zestawem klas Java, które można zainstalować w określonej wersji w instancji AEM. Te paczki pozwalają nam kontrolować, jakie inne zależności są używane w naszym kodzie. Ze strategicznego punktu widzenia przyczynia się to do poprawy łatwości utrzymania takiego kodu – bundle może definiować zawężenie używanych pakietów (tj. zestawu ścieżek klas Java), z którymi komunikuje się nasz kod.

Oprócz izolacji, OSGi dostarcza sposób na uruchamianie instancji klas – obiektów Java – zarządzając ich cyklem życia. Takie obiekty nazywane są komponentami – tym razem będą to OSGi Components. Można je utworzyć na podstawie konfigurowalnych warunków, zatrzymać i uruchomić dodatkowy kod aktywacyjny. Na przykład, jeśli potrzebujemy obiektu, który po uruchomieniu naszego bundle zapisze wiadomość na wyjście standardowe, możemy skonfigurować definicję klasy tak, aby stała się komponentem OSGi, którego metoda activate wykonuje taki właśnie kod.

Niewyobrażalnie trudną sytuacją byłby brak komunikacji pomiędzy komponentami. OSGi wprowadza koncepcję serwisu OSGi (OSGi Service), która może zadeklarować, że dany interfejs zostanie dostarczony do innych komponentów, używając takiej a takiej implementacji. Zasadniczo ten proces wstrzyknięcia serwisu odbywa się dynamicznie lub podczas tworzenia innego komponentu. Innymi słowy, komponenty mogą agregować serwisy OSGi i w razie potrzeby wywoływać ich metody.

Co w przypadku, gdy chcielibyśmy jednak obsłużyć webowe zapytania poza komponentami AEM?

Servlets i Filters

Architekci pracują w ograniczonych środowiskach, ograniczeniach i standardach, których należy przestrzegać. Czasami jesteśmy zmuszeni wyjść poza ramy zwykle stosowanych rozwiązań i dodać coś kompletnie unikatowego do oklepanej koncepcji. Niektórzy nazywają to sztuką, inni „wyjątkiem potwierdzającym regułę”.

Rozważmy scenariusz, w którym musimy wprowadzić JSON endpoint, aby zasilić komponent dostępności towaru dynamicznie pozyskiwanymi danymi z innego, zwykle wewnętrznego źródła. Wyżej zaobserwowaliśmy standardową sytuację, w której komponenty są producentami fragmentów HTML. Obecnie architekci powinni projektować intensywnie odwiedzane strony HTML jako statyczne, przetrzymywane w cache jak najbliżej użytkownika końcowego za pomocą Content Delivery Network (CDN).

Jak na razie podejście modułowości AEM nie daje możliwości połączenia tych dwóch wymagań ze sobą, chyba, że możemy mieć dodatkowe rozszerzenie wyłącznie dla tego komponentu i go wyrenderować na podstawie indywidualnej ścieżki. Czasami, biorąc pod uwagę fakt, że możemy utknąć ze zbyt długimi ścieżkami, nie wchodzi to w grę i musimy zrobić krok wstecz – użyć popularnego mechanizmu serwera WWW w świecie Javy, jakim jest Servlet.

Jeśli jesteś dobrze zakorzeniony w Java, to wiesz, że Servlet pociąga ze sobą inną koncepcję – Filter. Jeśli nie brzmi to znajomo, zalecam przeczytanie dokumentacji o Java Servlets i poeksperymentowanie z nimi poza AEM[5].

Różnica między zwykłymi Servlets a AEM-owymi polega na tym, że wszystkie są serwisami OSGi – mogą komunikować się z innymi serwisami, w efekcie z resztą systemu AEM. Architekt powinien wykorzystać ten fakt podczas projektowania zwięzłych rozwiązań, aby uniknąć powtarzania tej samej implementacji funkcjonalności w Servlets i Sling Models, wprowadzając albo współdzielone klasy Java, albo serwisy OSGi.

W ostatecznym rozrachunku, nawet przy tak wielu warstwach izolacji, system jest wartością dodaną samych bezpośrednich i pośrednich relacji między częściami, a nie wyłącznie sumą ich części.

Client Library

Strony internetowe to nie tylko pliki HTML czy dane JSON przekazywane użytkownikowi końcowemu. Wygląd strony jest równie ważny jak treść, co więcej w literaturze wygląd i styl jest nierozłączną częścią treści[6]. Architekt nie może pominąć tej części planowania projektu strony internetowej i komponentów.

Pomyśl na razie o plikach CSS – czy powinny być na stale zespawane z komponentami AEM? Innymi słowy, czy niektóre CSS mogą być tak samo wielokrotnego użytku jak komponenty? Biorąc pod uwagę fakt możliwości ponownego użycia, komponenty powinny zachowywać modyfikowalny wygląd i styl. Arkusze stylów są bardziej częścią strony (szablonu, którego nie poruszyliśmy w tym wpisie na blogu) niż komponentem.

A co ze skryptami JS? Zachowanie komponentów – zwłaszcza tych, które dynamicznie się zmieniają lub z którymi można wchodzić w interakcje bez ładowania strony – wydaje się być znacznie bliższe komponentom sensu stricte niż ich wyglądowi.

Biorąc pod uwagę tę rozbieżność, powinniśmy myśleć o zachowaniu i wyglądzie jako o luźno powiązanych ze sobą. W realnych systemach niektóre są ze sobą silnie połączone i nie można ich rozdzielić, jednak jest to raczej przypadek wyjątkowy niż powszechny.

Struktura Biblioteki Klienta
Ryc. 4 Struktura Biblioteki Klienta

W AEM wprowadzono mechanizm agregacji i zależności plików CSS i JS. Jest on identyfikowany przez ciąg znaków zwanym kategorią (ang. category). Przeglądarka ładuje wszystkie te pliki w tym samym kontekście strony (wyłączając z tego Shadow DOM), więc architekt powinien rozważyć ładowanie Client Libraries tylko w komponencie reprezentującym stronę HTML.

Client Libraries mogą definiować inne kategorie, które mają być zależne lub w których mają być osadzone. Daje to potężną możliwość definiowania wymaganych bibliotek do załadowania JS lub CSS, ale bez zrzucania odpowiedzialności za doładowanie brakujących bibliotek na konsumentów biblioteki. Dodatkowo, nie musisz niczego zmieniać na stronie, jeśli niektóre zależności będą przestarzałe i będziesz musiał zmienić kategorię zależności we własnej bibliotece.

Co zaskakujące, można nazwać wiele bibliotek tą samą nazwą kategorii i wszystkie zostaną załadowane na stronę (w losowej kolejności, ale jednak). Ta funkcja może być przydatna dla architektów wykorzystujących możliwość dodania dodatkowego CSS lub JS dla określonej kategorii, bez zmiany zawartości strony i szablonu. Pomyśl o tym jak o aspect programming, gdzie możesz wprowadzić dodatkowy kod CSS lub JS pomiędzy już istniejący.

Nie ma skodyfikowanych zasad strukturyzacji lub projektowania Client Libraries, jednak logicznym rozwiązaniem wydaje się utworzenie jednego do obsługi istotnego zachowania komponentów, a innego do wyglądu strony.

Podsumowanie

Te koncepcje są podstawą każdego porządnego systemu opracowanego w środowisku AEM. Nie daj się jednak zwieść – to dopiero początek Twojej podróży przez jego funkcje. Niemniej jednak to właśnie odmienne podejście do przetwarzania i architektury było pierwotnym powodem, dla którego AEM jest obecnie najbardziej innowacyjnym systemem CMS na rynku. Sprawia, że struktura jest znacznie bliższa treści niż podejście oparte wyłącznie na szablonach. Nie wyobrażasz sobie swojej strony bez szablonów? Nie martw się, w AEM też są szablony, ale zdecydowanie nie grają pierwszych skrzypiec, jeżeli chodzi o tworzenie treści.

Adobe Experience Manager to duży system CMS. W tym artykule nie odnosiłem się do funkcjonalności dostępnych w systemie od razu po wyjęciu z pudełka – chodziło mi o to, aby przedstawić narzędzia myślowe, których używam do planowania pracy tworzenia komponentów, których tam jeszcze nie ma lub nie może być, biorąc pod uwagę fakt, że z definicji biznes klientów rozwiązuje problemy w unikalny sposób.

Czas poćwiczyć!

Korzystając wyłącznie z tego wpisu, powinieneś być w stanie zbudować architekturę odpowiednią dla naprawdę interesujących przypadków. Jeśli obecnie ich nie masz, podam Ci kilka pomysłów, wyobraź sobie:

  • Witrynę biblioteki, w której każda książka jest stroną i może agregować strony zarówno według kategorii, które są naturalne i statyczne (jak gatunki), jak i tymczasowe, dynamiczne (np. „polecane dla Ciebie”).
  • Instytut naukowy publikujący każdego tygodnia setki stron, na których trzeba zaimplementować komponenty, które tworzą połączenie z zewnętrzną sztuczną inteligencją, ustalającą relacje pomiędzy tymi artykułami (zaplanuj połączenie do ChatGPT API).
  • Firmę w sektorze finansowym, w której musisz wprowadzić komponent oparty na pozostałej części strony, który daje wskazówki dotyczące potencjalnych opcji inwestycyjnych używający AI lub silnikiem BI skonstruowany wewnątrz firmy. Zauważ, że używanie zewnętrznego API i wewnętrznego ma ogromne konsekwencje w tworzeniu wewnętrznego interfejsu do jego obsługi, a także wprowadzenia dodatkowych bundles.

Projektowanie architektury dla rozwiązań opartych na AEM to świetna zabawa, kiedy ma się odpowiednie narzędzia myślowe i dużo ćwiczy. Masz już narzędzia, możesz się wziąć za trening!


[1] N. Nan i S. Kumar, „Joint Effect of Team Structure and Software Architecture in Open Source Software Development„, w: IEEE Transactions on Engineering Management, tom 60, nr 3, s. 592-603, sierpień 2013 r.

[2] https://www.aemcomponents.dev/

[3] https://en.wikipedia.org/wiki/OSGi

[4] https://docs.osgi.org/specification/osgi.core/8.0.0/toc.html

[5] https://www.baeldung.com/intro-to-servlets

[6] Barker, Deane. Zarządzanie treścią internetową: systemy, funkcje i najlepsze praktyki. „O’Reilly Media, Inc.”, 2016.

***

Jeśli interesują Cię rozwiązania Adobe, zajrzyj koniecznie również do innych artykułów naszych ekspertów.

5/5 ( głosy: 3)
Ocena:
5/5 ( głosy: 3)
Autor
Avatar
Dawid Pura

Inżynier oprogramowania specjalizujący się w dostarczaniu i utrzymywaniu systemów CMS opartych na Adobe Experience Manager. W swojej karierze zrewolucjonizował tyko niektóre projekty nad którymi pracował, ale na pewno miał wpływ na wszystkie. Pracując w branży od 10 lat, nie ma wątpliwości, że tworzenie oprogramowania jest wciąż nieznane, organiczne i wyjątkowo ciekawe. Zakłada kapelusz filozofa programowania, kiedy to tylko możliwe

Zostaw komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

Może Cię również zainteresować

Pokaż więcej artykułów

Bądź na bieżąco

Zasubskrybuj naszego bloga i otrzymuj informacje o najnowszych wpisach.

Otrzymaj ofertę

Jeśli chcesz dowiedzieć się więcej na temat oferty Sii, skontaktuj się z nami.

Wyślij zapytanie Wyślij zapytanie

Natalia Competency Center Director

Get an offer

Dołącz do Sii

Znajdź idealną pracę – zapoznaj się z naszą ofertą rekrutacyjną i aplikuj.

Aplikuj Aplikuj

Paweł Process Owner

Join Sii

ZATWIERDŹ

This content is available only in one language version.
You will be redirected to home page.

Are you sure you want to leave this page?