Subscription Manager to aplikacja webowa, którą zbudowałem do zarządzania cyklicznymi subskrypcjami usług cyfrowych i śledzenia ich kosztów.
Na początku była dość prostą aplikacją .NET.
Z czasem pojawiły się w niej aplikacja webowa w Blazorze, API, persystencja, uwierzytelnianie, testy, infrastruktura chmurowa i kilka zewnętrznych integracji.
Kiedy zacząłem myśleć o kolejnej aplikacji, część tych elementów wyglądała na wystarczająco użyteczną, żeby wykorzystać je ponownie.
Najprościej byłoby skopiować cały projekt. Razem z nim przeniósłbym jednak wiele rzeczy, które należały konkretnie do Subscription Managera.
To był jeden z powodów, dla których zacząłem pracować nad Launchpadem.
Zaczynając od czegoś, co było już używane
W tamtym momencie Subscription Manager przeszedł już kilka iteracji.
Miałem działającą komunikację między Webem i API, uwierzytelnianie, zarządzanie kontem, testy oraz aplikację Blazor komunikującą się z API przez HTTP.
Nie oznaczało to, że taka struktura jest właściwa dla każdej aplikacji .NET. Oznaczało tylko, że użyłem jej już w prawdziwym projekcie i wiedziałem, jak zachowuje się w praktyce.
To czyniło ją użytecznym punktem wyjścia.
Launchpad zaczął od ponownego wykorzystania części tych rozwiązań i pozostawienia tych, które miały sens wyłącznie w Subscription Managerze.
Dobrym przykładem było Identity.
W Subscription Managerze stopniowo przejęło odpowiedzialność za funkcje związane z konkretnym produktem: preferencje użytkownika, potwierdzanie adresu e-mail, reset hasła, plany subskrypcji i inne ustawienia konta.
Pierwsza wersja Launchpada potrzebowała znacznie mniej.
Zachowałem ogólne podejście, ale nie całą funkcjonalność, która z czasem narosła wokół niego.
Podobnie było w innych miejscach.
| Wykorzystane jako punkt wyjścia | Pozostawione w Subscription Managerze |
|---|---|
| Komunikacja Web z API | Domena subskrypcji |
| ASP.NET Core Identity | Katalog usług cyfrowych |
| Obsługa bieżącego użytkownika | Integracja ze Stripe |
| Cykl życia konta | Integracja z kursami walut |
| Konwencje testów | Funkcjonalność planu oszczędnościowego |
| Model interakcji w Blazorze | Ekrany specyficzne dla produktu |
Celem nie było znalezienie wszystkiego, co oba projekty mogłyby współdzielić.
Chodziło przede wszystkim o to, żeby nie przenosić elementów tylko dlatego, że już istniały.
Nie każda warstwa musiała przejść dalej
Małym przykładem jest projekt Domain.
Subscription Manager go ma, ponieważ zawiera rzeczywiste zachowanie domenowe związane z subskrypcjami i usługami cyfrowymi.
Współdzielony Template Launchpada obecnie go nie ma.
Mógłbym dodać pusty projekt Domain, żeby struktura wyglądała bardziej znajomo, ale nie było w nim nic użytecznego do umieszczenia.
Dlatego go pominąłem.
Wygenerowana aplikacja może go dodać, kiedy jej domena będzie tego potrzebowała. Launchpad również może z czasem zyskać współdzielony kod domenowy, jeśli pojawi się rzeczywisty przypadek użycia.
Na razie nie miałem konkretnej potrzeby dla tej warstwy.
Generator pojawił się później
Nie chciałem też budować generatora zbyt wcześnie.
Na początku nie wiedziałem jeszcze, które części fundamentu pozostaną stabilne. Dodawałbym wtedy konfigurację dla przypadków, z którymi jeszcze się nie zetknąłem.
Wcześniejszy plan był więc prostszy: dalej pracować z fundamentem, używać go w konkretnych projektach i obserwować, co rzeczywiście zaczyna się powtarzać.
Ten plan zmienił się, gdy Launchpad stał się bardziej konkretny.
W repozytorium z czasem wyraźnie rozdzieliły się dwie części:
Launchpad
├── Template
│ └── fundament aplikacji do ponownego wykorzystania
│
└── Generator
└── narzędzia używane do tworzenia aplikacji
Dzięki temu granica stała się łatwiejsza do zrozumienia.
Template jest częścią, która może stać się nową aplikacją.
Generator to narzędzia obsługujące ten proces i pozostające w Launchpadzie.
Kiedy ten podział był już jasny, generowanie nie musiało rozwiązywać bardzo ogólnego problemu. Mogło działać na strukturze, którą Launchpad już znał.
Przewidywalna praca powinna pozostać przewidywalna
Launchpad jest też miejscem, w którym eksperymentuję z pracą inżynierską wspieraną przez AI.
Generowanie repozytorium okazało się pod tym względem ciekawym przypadkiem.
Są elementy tworzenia nowego produktu, które nie wymagają interpretacji. Zmiana nazw projektów i przestrzeni nazw, aktualizacja znanych identyfikatorów, ponowne zbudowanie rozwiązania i uruchomienie testów mają dość jasno określony oczekiwany wynik.
Wolałem, żeby te kroki pozostały deterministyczne.
Obecny przepływ wygląda mniej więcej tak:
Definicja produktu
↓
Kopiowanie Template
↓
Transformacja
↓
Walidacja
↓
Pakowanie
↓
Publikacja
Te kroki są zwykłym kodem .NET.
AI nadal może być przydatne później, kiedy praca staje się mniej przewidywalna: przy zachowaniu specyficznym dla produktu, decyzjach dotyczących UI albo zmianach, w których trzeba coś zinterpretować, a nie tylko przekształcić.
Takie rozdzielenie jest dla mnie użyteczne w tym projekcie. Sprawia też, że błędy w procesie generowania są znacznie mniej tajemnicze.
To nadal eksperyment
Launchpad nie jest gotowym frameworkiem dla każdej aplikacji .NET.
Template zawiera rzeczy, które do tej pory okazały się użyteczne. Generator potrafi przygotować, przekształcić, zweryfikować, spakować i opublikować produkt oparty na tym fundamencie.
Następnym wartościowym sprawdzianem będzie ponowne użycie tego fundamentu i zobaczenie, co przetrwa.
Część elementów może okazać się rzeczywiście wielokrotnego użytku. Inne mogą wrócić do poszczególnych produktów. Coś, co dziś wydaje się przydatne, może zniknąć całkowicie.
Subscription Manager dał mi konkretną aplikację, od której mogłem zacząć. Launchpad daje mi miejsce do sprawdzania, które części tego doświadczenia naprawdę warto przenosić dalej.
Dla mnie okazało się to bardziej użyteczne niż próba zaprojektowania fundamentu do ponownego wykorzystania, zanim miałem coś rzeczywistego do ponownego wykorzystania.