Tutustu heksagonaaliseen ja puhtaaseen arkkitehtuuriin ylläpidettävien, skaalautuvien ja testattavien frontend-sovellusten rakentamiseksi. Opi niiden periaatteet, hyödyt ja käytännön toteutusstrategiat.
Frontend-arkkitehtuuri: Heksagonaalinen ja puhdas arkkitehtuuri skaalautuville sovelluksille
Frontend-sovellusten monimutkaistuessa hyvin määritelty arkkitehtuuri on välttämätön ylläpidettävyyden, testattavuuden ja skaalautuvuuden kannalta. Kaksi suosittua arkkitehtuurimallia, jotka vastaavat näihin haasteisiin, ovat heksagonaalinen arkkitehtuuri (tunnetaan myös nimellä portit ja adapterit) ja puhdas arkkitehtuuri. Vaikka nämä periaatteet ovat peräisin backend-maailmasta, niitä voidaan soveltaa tehokkaasti frontend-kehitykseen vankkojen ja mukautuvien käyttöliittymien luomiseksi.
Mitä on frontend-arkkitehtuuri?
Frontend-arkkitehtuuri määrittelee frontend-sovelluksen eri komponenttien rakenteen, organisaation ja vuorovaikutuksen. Se tarjoaa suunnitelman sovelluksen rakentamiselle, ylläpidolle ja skaalaamiselle. Hyvä frontend-arkkitehtuuri edistää:
- Ylläpidettävyyttä: Koodia on helpompi ymmärtää, muokata ja debugata.
- Testattavuutta: Helpottaa yksikkö- ja integraatiotestien kirjoittamista.
- Skaalautuvuutta: Mahdollistaa sovelluksen käsittelemään kasvavaa monimutkaisuutta ja käyttäjäkuormaa.
- Uudelleenkäytettävyyttä: Edistää koodin uudelleenkäyttöä sovelluksen eri osissa.
- Joustavuutta: Mukautuu muuttuviin vaatimuksiin ja uusiin teknologioihin.
Ilman selkeää arkkitehtuuria frontend-projektit voivat nopeasti muuttua monoliittisiksi ja vaikeasti hallittaviksi, mikä johtaa kasvaneisiin kehityskustannuksiin ja heikentyneeseen ketteryyteen.
Johdanto heksagonaaliseen arkkitehtuuriin
Alistair Cockburnin ehdottama heksagonaalinen arkkitehtuuri pyrkii irrottamaan sovelluksen ydinliiketoimintalogiikan ulkoisista riippuvuuksista, kuten tietokannoista, käyttöliittymäkehyksistä ja kolmannen osapuolen API-rajapinnoista. Tämä saavutetaan porttien ja adapterien konseptin avulla.
Heksagonaalisen arkkitehtuurin avainkäsitteet:
- Ydin (Domain): Sisältää sovelluksen liiketoimintalogiikan ja käyttötapaukset. Se on riippumaton kaikista ulkoisista kehyksistä tai teknologioista.
- Portit: Rajapinnat, jotka määrittelevät, miten ydin on vuorovaikutuksessa ulkomaailman kanssa. Ne edustavat ytimen syöte- ja tulosterajoja.
- Adapterit: Porttien toteutukset, jotka yhdistävät ytimen tiettyihin ulkoisiin järjestelmiin. Adaptereita on kahdenlaisia:
- Ohjaavat adapterit (Primary Adapters): Käynnistävät vuorovaikutuksen ytimen kanssa. Esimerkkejä ovat käyttöliittymäkomponentit, komentoriviliittymät tai muut sovellukset.
- Ohjattavat adapterit (Secondary Adapters): Ydin kutsuu niitä vuorovaikutukseen ulkoisten järjestelmien kanssa. Esimerkkejä ovat tietokannat, API-rajapinnat tai tiedostojärjestelmät.
Ydin ei tiedä mitään tietyistä adaptereista. Se on vuorovaikutuksessa niiden kanssa vain porttien kautta. Tämä irrottaminen mahdollistaa erilaisten adapterien helpon vaihtamisen vaikuttamatta ydinlogiikkaan. Voit esimerkiksi vaihtaa yhdestä käyttöliittymäkehyksestä (esim. React) toiseen (esim. Vue.js) yksinkertaisesti vaihtamalla ohjaavan adapterin.
Heksagonaalisen arkkitehtuurin hyödyt:
- Parempi testattavuus: Ydinliiketoimintalogiikka voidaan helposti testata eristyksissä ilman riippuvuutta ulkoisista järjestelmistä. Voit käyttää mock-adaptereita ulkoisten järjestelmien käyttäytymisen simulointiin.
- Parempi ylläpidettävyys: Muutoksilla ulkoisiin järjestelmiin on minimaalinen vaikutus ydinlogiikkaan. Tämä helpottaa sovelluksen ylläpitoa ja kehittämistä ajan myötä.
- Suurempi joustavuus: Voit helposti mukauttaa sovelluksen uusiin teknologioihin ja vaatimuksiin lisäämällä tai vaihtamalla adaptereita.
- Parannettu uudelleenkäytettävyys: Ydinliiketoimintalogiikkaa voidaan käyttää uudelleen eri konteksteissa yhdistämällä se eri adaptereihin.
Johdanto puhtaaseen arkkitehtuuriin
Puhdas arkkitehtuuri, jonka Robert C. Martin (Uncle Bob) on tehnyt tunnetuksi, on toinen arkkitehtuurimalli, joka korostaa vastuualueiden erottamista ja irrottamista. Se keskittyy luomaan järjestelmän, joka on riippumaton kehyksistä, tietokannoista, käyttöliittymästä ja kaikista ulkoisista tekijöistä.
Puhtaan arkkitehtuurin avainkäsitteet:
Puhdas arkkitehtuuri järjestää sovelluksen samankeskisiin kerroksiin, joissa abstraktein ja uudelleenkäytettävin koodi on keskellä ja konkreettisin ja teknologiaspesifisin koodi ulkokerroksissa.
- Entiteetit: Edustavat sovelluksen ydinliiketoiminnan objekteja ja sääntöjä. Ne ovat riippumattomia kaikista ulkoisista järjestelmistä.
- Käyttötapaukset: Määrittelevät sovelluksen liiketoimintalogiikan ja sen, miten käyttäjät ovat vuorovaikutuksessa järjestelmän kanssa. Ne orkestroivat entiteettejä suorittamaan tiettyjä tehtäviä.
- Rajapinta-adapterit: Muuntavat dataa käyttötapausten ja ulkoisten järjestelmien välillä. Tämä kerros sisältää presenterit, kontrollerit ja gatewayt.
- Kehykset ja ajurit: Uloin kerros, joka sisältää käyttöliittymäkehyksen, tietokannan ja muut ulkoiset teknologiat.
Puhtaan arkkitehtuurin riippuvuussääntö sanoo, että ulommat kerrokset voivat riippua sisemmistä kerroksista, mutta sisemmät kerrokset eivät voi riippua ulommista kerroksista. Tämä varmistaa, että ydinliiketoimintalogiikka on riippumaton kaikista ulkoisista kehyksistä tai teknologioista.
Puhtaan arkkitehtuurin hyödyt:
- Riippumaton kehyksistä: Arkkitehtuuri ei ole riippuvainen jonkin ominaisuuksilla ladatun ohjelmistokirjaston olemassaolosta. Tämä antaa sinun käyttää kehyksiä työkaluina sen sijaan, että joutuisit pakottamaan järjestelmäsi niiden rajoitettuihin puitteisiin.
- Testattava: Liiketoimintasäännöt voidaan testata ilman käyttöliittymää, tietokantaa, verkkopalvelinta tai mitään muuta ulkoista elementtiä.
- Riippumaton käyttöliittymästä: Käyttöliittymä voidaan vaihtaa helposti muuttamatta muuta järjestelmää. Verkkokäyttöliittymä voidaan korvata konsolikäyttöliittymällä muuttamatta mitään liiketoimintasääntöjä.
- Riippumaton tietokannasta: Voit vaihtaa Oraclen tai SQL Serverin Mongoon, BigTableen, CouchDB:hen tai johonkin muuhun. Liiketoimintasääntösi eivät ole sidottuja tietokantaan.
- Riippumaton kaikista ulkoisista tekijöistä: Itse asiassa liiketoimintasääntösi eivät yksinkertaisesti tiedä *mitään* at all about the outside world.
Heksagonaalisen ja puhtaan arkkitehtuurin soveltaminen frontend-kehitykseen
Vaikka heksagonaalinen ja puhdas arkkitehtuuri yhdistetään usein backend-kehitykseen, niiden periaatteita voidaan tehokkaasti soveltaa frontend-sovelluksiin niiden arkkitehtuurin ja ylläpidettävyyden parantamiseksi. Näin se tapahtuu:
1. Tunnista ydin (Domain)
Ensimmäinen askel on tunnistaa frontend-sovelluksesi ydinliiketoimintalogiikka. Tähän kuuluvat entiteetit, käyttötapaukset ja liiketoimintasäännöt, jotka ovat riippumattomia käyttöliittymäkehyksestä tai ulkoisista API-rajapinnoista. Esimerkiksi verkkokauppasovelluksessa ydin voisi sisältää logiikan tuotteiden, ostoskorien ja tilausten hallintaan.
Esimerkki: Tehtävänhallintasovelluksessa ydin-domain voisi koostua seuraavista:
- Entiteetit: Tehtävä, Projekti, Käyttäjä
- Käyttötapaukset: LuoTehtävä, PäivitäTehtävä, MääritäTehtävä, SuoritaTehtävä, ListaaTehtävät
- Liiketoimintasäännöt: Tehtävällä on oltava otsikko, tehtävää ei voi määrittää käyttäjälle, joka ei ole projektin jäsen.
2. Määrittele portit ja adapterit (heksagonaalinen arkkitehtuuri) tai kerrokset (puhdas arkkitehtuuri)
Seuraavaksi määrittele portit ja adapterit (heksagonaalinen arkkitehtuuri) tai kerrokset (puhdas arkkitehtuuri), jotka erottavat ytimen ulkoisista järjestelmistä. Frontend-sovelluksessa näitä voivat olla:
- Käyttöliittymäkomponentit (Ohjaavat adapterit/Kehykset & Ajurit): React-, Vue.js-, Angular-komponentit, jotka ovat vuorovaikutuksessa käyttäjän kanssa.
- API-asiakkaat (Ohjattavat adapterit/Rajapinta-adapterit): Palvelut, jotka tekevät pyyntöjä backendin API-rajapintoihin.
- Tietovarastot (Ohjattavat adapterit/Rajapinta-adapterit): Paikallinen tallennus, IndexedDB tai muut tiedon tallennusmekanismit.
- Tilan hallinta (Rajapinta-adapterit): Redux, Vuex tai muut tilanhallintakirjastot.
Esimerkki heksagonaalisella arkkitehtuurilla:
- Ydin: Tehtävänhallintalogiikka (entiteetit, käyttötapaukset, liiketoimintasäännöt).
- Portit:
TaskService(määrittelee metodit tehtävien luomiseen, päivittämiseen ja hakemiseen). - Ohjaava adapteri: React-komponentit, jotka käyttävät
TaskService-palvelua vuorovaikutuksessa ytimen kanssa. - Ohjattava adapteri: API-asiakas, joka toteuttaa
TaskService-palvelun ja tekee pyyntöjä backendin API-rajapintaan.
Esimerkki puhtaalla arkkitehtuurilla:
- Entiteetit: Tehtävä, Projekti, Käyttäjä (puhtaita JavaScript-objekteja).
- Käyttötapaukset: CreateTaskUseCase, UpdateTaskUseCase (orkestroivat entiteettejä).
- Rajapinta-adapterit:
- Kontrollerit: Käsittelevät käyttäjän syötteet käyttöliittymästä.
- Presenterit: Muotoilevat datan näytettäväksi käyttöliittymässä.
- Gatewayt: Ovat vuorovaikutuksessa API-asiakkaan kanssa.
- Kehykset ja ajurit: React-komponentit, API-asiakas (axios, fetch).
3. Toteuta adapterit (heksagonaalinen arkkitehtuuri) tai kerrokset (puhdas arkkitehtuuri)
Nyt toteuta adapterit tai kerrokset, jotka yhdistävät ytimen ulkoisiin järjestelmiin. Varmista, että adapterit tai kerrokset ovat riippumattomia ytimestä ja että ydin on vuorovaikutuksessa niiden kanssa vain porttien tai rajapintojen kautta. Tämä mahdollistaa erilaisten adapterien tai kerrosten helpon vaihtamisen vaikuttamatta ydinlogiikkaan.
Esimerkki (Heksagonaalinen arkkitehtuuri):
// TaskService-portti
interface TaskService {
createTask(taskData: TaskData): Promise;
updateTask(taskId: string, taskData: TaskData): Promise;
getTask(taskId: string): Promise;
}
// API-asiakasadapteri
class ApiTaskService implements TaskService {
async createTask(taskData: TaskData): Promise {
// Tee API-pyyntö tehtävän luomiseksi
}
async updateTask(taskId: string, taskData: TaskData): Promise {
// Tee API-pyyntö tehtävän päivittämiseksi
}
async getTask(taskId: string): Promise {
// Tee API-pyyntö tehtävän hakemiseksi
}
}
// React-komponenttiadapteri
function TaskList() {
const taskService: TaskService = new ApiTaskService();
const handleCreateTask = async (taskData: TaskData) => {
await taskService.createTask(taskData);
// Päivitä tehtävälista
};
// ...
}
Esimerkki (Puhdas arkkitehtuuri):
// Entiteetit
class Task {
constructor(public id: string, public title: string, public description: string) {}
}
// Käyttötapaus
class CreateTaskUseCase {
constructor(private taskGateway: TaskGateway) {}
async execute(title: string, description: string): Promise {
const task = new Task(generateId(), title, description);
await this.taskGateway.create(task);
return task;
}
}
// Rajapinta-adapterit - Gateway
interface TaskGateway {
create(task: Task): Promise;
}
class ApiTaskGateway implements TaskGateway {
async create(task: Task): Promise {
// Tee API-pyyntö tehtävän luomiseksi
}
}
// Rajapinta-adapterit - Kontrolleri
class TaskController {
constructor(private createTaskUseCase: CreateTaskUseCase) {}
async createTask(req: Request, res: Response) {
const { title, description } = req.body;
const task = await this.createTaskUseCase.execute(title, description);
res.json(task);
}
}
// Kehykset & Ajurit - React-komponentti
function TaskForm() {
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
const apiTaskGateway = new ApiTaskGateway();
const createTaskUseCase = new CreateTaskUseCase(apiTaskGateway);
const taskController = new TaskController(createTaskUseCase);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
await taskController.createTask({ body: { title, description } } as Request, { json: (data: any) => console.log(data) } as Response);
};
return (
);
}
4. Toteuta riippuvuuksien injektointi
Irrottaaksesi ytimen entisestään ulkoisista järjestelmistä, käytä riippuvuuksien injektointia tarjotaksesi adapterit tai kerrokset ytimelle. Tämä mahdollistaa adapterien tai kerrosten eri toteutusten helpon vaihtamisen muuttamatta ydinkoodia.
Esimerkki:
// Injektoi TaskService TaskList-komponenttiin
function TaskList(props: { taskService: TaskService }) {
const { taskService } = props;
const handleCreateTask = async (taskData: TaskData) => {
await taskService.createTask(taskData);
// Päivitä tehtävälista
};
// ...
}
// Käyttö
const apiTaskService = new ApiTaskService();
5. Kirjoita yksikkötestejä
Yksi heksagonaalisen ja puhtaan arkkitehtuurin keskeisistä eduista on parantunut testattavuus. Voit helposti kirjoittaa yksikkötestejä ydinliiketoimintalogiikalle ilman riippuvuutta ulkoisista järjestelmistä. Käytä mock-adaptereita tai -kerroksia simuloidaksesi ulkoisten järjestelmien käyttäytymistä ja varmistaaksesi, että ydinlogiikka toimii odotetusti.
Esimerkki:
// Mock TaskService
class MockTaskService implements TaskService {
async createTask(taskData: TaskData): Promise {
return Promise.resolve({ id: '1', ...taskData });
}
async updateTask(taskId: string, taskData: TaskData): Promise {
return Promise.resolve({ id: taskId, ...taskData });
}
async getTask(taskId: string): Promise {
return Promise.resolve({ id: taskId, title: 'Test Task', description: 'Test Description' });
}
}
// Yksikkötesti
describe('TaskList', () => {
it('pitäisi luoda tehtävä', async () => {
const mockTaskService = new MockTaskService();
const taskList = new TaskList({ taskService: mockTaskService });
const taskData = { title: 'New Task', description: 'New Description' };
const newTask = await taskList.handleCreateTask(taskData);
expect(newTask.title).toBe('New Task');
expect(newTask.description).toBe('New Description');
});
});
Käytännön huomioita ja haasteita
Vaikka heksagonaalinen ja puhdas arkkitehtuuri tarjoavat merkittäviä etuja, niiden soveltamisessa frontend-kehitykseen on myös joitakin käytännön huomioita ja haasteita:
- Lisääntynyt monimutkaisuus: Nämä arkkitehtuurit voivat lisätä monimutkaisuutta koodipohjaan, erityisesti pienissä tai yksinkertaisissa sovelluksissa.
- Oppimiskäyrä: Kehittäjien saattaa olla tarpeen oppia uusia käsitteitä ja malleja toteuttaakseen näitä arkkitehtuureja tehokkaasti.
- Ylisuunnittelu: On tärkeää välttää sovelluksen ylisuunnittelua. Aloita yksinkertaisella arkkitehtuurilla ja lisää monimutkaisuutta vähitellen tarpeen mukaan.
- Abstraktion tasapainottaminen: Oikean abstraktiotason löytäminen voi olla haastavaa. Liika abstraktio voi tehdä koodista vaikeasti ymmärrettävää, kun taas liian vähäinen abstraktio voi johtaa tiukkaan kytkentään.
- Suorituskykyyn liittyvät huomiot: Liialliset abstraktiokerrokset voivat mahdollisesti vaikuttaa suorituskykyyn. On tärkeää profiloida sovellus ja tunnistaa mahdolliset suorituskyvyn pullonkaulat.
Kansainvälisiä esimerkkejä ja sovituksia
Heksagonaalisen ja puhtaan arkkitehtuurin periaatteet soveltuvat frontend-kehitykseen maantieteellisestä sijainnista tai kulttuurisesta kontekstista riippumatta. Kuitenkin tietyt toteutukset ja sovitukset voivat vaihdella projektin vaatimusten ja kehitystiimin mieltymysten mukaan.
Esimerkki 1: Globaali verkkokauppa-alusta
Globaali verkkokauppa-alusta voisi käyttää heksagonaalista arkkitehtuuria irrottaakseen ytimen ostoskori- ja tilaustenhallintalogiikan käyttöliittymäkehyksestä ja maksuyhdyskäytävistä. Ydin vastaisi tuotteiden hallinnasta, hintojen laskemisesta ja tilausten käsittelystä. Ohjaavat adapterit sisältäisivät React-komponentit tuoteluettelolle, ostoskorille ja kassasivuille. Ohjattavat adapterit sisältäisivät API-asiakkaat eri maksuyhdyskäytäville (esim. Stripe, PayPal, Alipay) ja kuljetuspalveluille (esim. FedEx, DHL, UPS). Tämä mahdollistaa alustan helpon mukautumisen eri alueellisiin maksutapoihin ja toimitusvaihtoehtoihin.
Esimerkki 2: Monikielinen sosiaalisen median sovellus
Monikielinen sosiaalisen median sovellus voisi käyttää puhdasta arkkitehtuuria erottaakseen ytimen käyttäjätunnistus- ja sisällönhallintalogiikan käyttöliittymä- ja lokalisointikehyksistä. Entiteetit edustaisivat käyttäjiä, julkaisuja ja kommentteja. Käyttötapaukset määrittelisivät, miten käyttäjät luovat, jakavat ja ovat vuorovaikutuksessa sisällön kanssa. Rajapinta-adapterit hoitaisivat sisällön kääntämisen eri kielille ja datan muotoilun eri käyttöliittymäkomponenteille. Tämä mahdollistaa sovelluksen helpon uusien kielten tukemisen ja mukautumisen erilaisiin kulttuurisiin mieltymyksiin.
Yhteenveto
Heksagonaalinen ja puhdas arkkitehtuuri tarjoavat arvokkaita periaatteita ylläpidettävien, testattavien ja skaalautuvien frontend-sovellusten rakentamiseen. Irrottamalla ydinliiketoimintalogiikan ulkoisista riippuvuuksista voit luoda joustavamman ja mukautuvamman koodipohjan, jota on helpompi kehittää ajan myötä. Vaikka nämä arkkitehtuurit saattavat lisätä alkuvaiheen monimutkaisuutta, pitkän aikavälin hyödyt ylläpidettävyyden, testattavuuden ja skaalautuvuuden osalta tekevät niistä kannattavan investoinnin monimutkaisissa frontend-projekteissa. Muista aloittaa yksinkertaisella arkkitehtuurilla ja lisätä monimutkaisuutta vähitellen tarpeen mukaan, sekä harkita huolellisesti mukana olevia käytännön näkökohtia ja haasteita.
Omaksumalla nämä arkkitehtuurimallit frontend-kehittäjät voivat rakentaa vankempia ja luotettavampia sovelluksia, jotka vastaavat käyttäjien muuttuviin tarpeisiin ympäri maailmaa.
Lisälukemistoa
- Hexagonal Architecture: https://alistaircockburn.com/hexagonal-architecture/
- Clean Architecture: https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html