Sådan lærer du software design og arkitektur - en køreplan

Denne artikel er et resumé af, hvad jeg skriver om i mit nyeste projekt, solidbook.io - Handbook to Software Design and Architecture with TypeScript. Tjek det, hvis du kan lide dette indlæg.

Det er vildt for mig at overveje det faktum, at Facebook engang var en tom tekstfil på andres computer.

Lol.

I det forløbne år har jeg gået hårdt inden for software design og arkitektur, Domain-Driven Design og skrevet en bog om det, og jeg ville tage et øjeblik for at prøve at samle det i noget nyttigt, jeg kunne dele med samfundet .

Her er min køreplan for, hvordan man lærer softwaredesign og arkitektur.

Jeg har opdelt det i to artefakter: stakken og kortet .

Stakken

I lighed med OSI-modellen i netværk bygger hvert lag oven på fundamentet til det forrige.

Stakken

Kortet

Selvom jeg synes, stakken er god til at se det større billede af, hvordan alt fungerer sammen, er kortet lidt mere detaljeret (og inspireret af køreplanen for webudviklere), og som et resultat synes jeg det er mere nyttigt.

Her er det nedenfor! For at forkaste repoen, læs min detaljerede opskrivning og download den i høj opløsning, klik her.

Køreplan for software design og arkitektur

Trin 1: Rens kode

Det allerførste skridt mod oprettelse af langvarig software er at finde ud af, hvordan man skriver ren kode .

Ren kode er kode, der er let at forstå og ændre. På lavt niveau manifesterer dette sig i et par designvalg som:

  • være konsekvent
  • foretrækker meningsfuld variabel, metode og klassenavne frem for at skrive kommentarer
  • at sikre, at koden er indrykket og anbragt korrekt
  • sikre, at alle testene kan køre
  • skriver rene funktioner uden bivirkninger
  • passerer ikke null

At skrive ren kode er utrolig vigtig.

Tænk på det som et spil jenga.

For at holde strukturen i vores projekt stabil over tid betaler ting som indrykning, små klasser og metoder og meningsfulde navne meget i det lange løb.

Den bedste ressource til at lære at skrive ren kode er Onkel Bobs bog, "Clean Code".

Trin 2: Programmering af paradigmer

Nu hvor vi skriver læsbar kode, der er let at vedligeholde, ville det være en god ide at virkelig forstå de 3 store programmeringsparadigmer og den måde, de påvirker, hvordan vi skriver kode.

I onkel Bobs bog "Ren arkitektur" henleder han opmærksomheden på, at:

  • Objektorienteret programmering er det værktøj, der er bedst egnet til at definere, hvordan vi krydser arkitektoniske grænser med polymorhpisme og plugins
  • Funktionel programmering er det værktøj, vi bruger til at skubbe data til grænserne for vores applikationer
  • og struktureret programmering er det værktøj, vi bruger til at skrive algoritmer

Dette indebærer, at effektiv software bruger en hybrid alle 3 programmeringsparadigmastile på forskellige tidspunkter.

Mens du kunne tage en strengt funktionel eller strengt objektorienteret tilgang til skrivning af kode, vil forståelse af, hvor hvert excel forbedrer kvaliteten af ​​dine designs.

Hvis alt hvad du har er en hammer, virker alt som et søm.

Ressourcer

For funktionel programmering , se:

  • Professor Frisbys mest passende guide til funktionel programmering
  • Domænemodellering er funktionel

Trin 3: Objektorienteret programmering

Det er vigtigt at vide, hvordan hvert af paradigmerne fungerer, og hvordan de opfordrer dig til at strukturere koden i dem, men med hensyn til arkitektur er objektorienteret programmering det klare værktøj til jobbet .

Ikke kun gør objektorienteret programmering os i stand til at oprette en plugin-arkitektur og bygge fleksibilitet i vores projekter; OOP leveres med de 4 principper for OOP (indkapsling, arv, polymorhisme og abstraktion), der hjælper os med at skabe rige domænemodeller .

De fleste udviklere, der lærer objektorienteret programmering, kommer aldrig til denne del: at lære at oprette en softwareimplementering af problemdomænet og lokalisere det i midten af ​​en lagdelt webapp.

Funktionel programmering kan virke som middel til alle formål i dette scenarie, men jeg vil anbefale at blive bekendt med model-driven design og Domain-Driven Design for at forstå det større billede af, hvordan objekt-modellerere er i stand til at indkapsle en hel virksomhed i nul-afhængigheds domænemodel.

Hvorfor er det en enorm aftale?

Det er enormt, for hvis du kan oprette en mental model for en virksomhed, kan du oprette en softwareimplementering af den forretning.

Trin 4: Designprincipper

På dette tidspunkt forstår du, at objektorienteret programmering er meget nyttigt til indkapsling af rige domænemodeller og løsning af den 3. type "problemer med hård software" - komplekse domæner.

Men OOP kan introducere nogle designudfordringer.

Hvornår skal jeg bruge komposition?

Hvornår skal jeg bruge arv?

Hvornår skal jeg bruge en abstrakt klasse?

Designprincipper er virkelig veletablerede og kamptestede objektorienterede bedste praksis, som du bruger som jernbanevagter.

Nogle eksempler på almindelige designprincipper, du bør gøre dig bekendt med, er:

  • Sammensætning over arv
  • Indkapsl det, der varierer
  • Program mod abstraktioner, ikke konkretioner
  • Hollywood-princippet: "Ring ikke til os, vi ringer til dig"
  • SOLID-principperne, især princippet om et enkelt ansvar
  • TØRR (gentag ikke dig selv)
  • YAGNI (Du har ikke brug for det)

Sørg dog for at komme til dine egne konklusioner. Følg ikke bare, hvad en anden siger, at du skal gøre. Sørg for, at det giver mening for dig.

Trin 5: Designmønstre

Næsten ethvert problem i software er allerede blevet kategoriseret og løst. Vi kalder disse mønstre: designmønstre faktisk.

Der er 3 kategorier af designmønstre: skabelses- , struktur- og adfærd .

Skabende

Skabelsesmønstre er mønstre, der styrer, hvordan objekter oprettes.

Eksempler på skabelsesmønstre inkluderer:

  • Den Singleton mønster , for at sikre kun en enkelt forekomst af en klasse kan eksistere
  • Den Abstract Factory mønster , for at skabe en instans af flere familier af klasser
  • The Prototype mønster , for at starte ud med en instans, der er klonet fra et eksisterende

Strukturel

Strukturelle mønstre er mønstre, der forenkler, hvordan vi definerer forholdet mellem komponenter.

Eksempler på strukturelle designmønstre inkluderer:

  • Den Adapter mønster , for at skabe en grænseflade for at aktivere klasser, der normalt ikke kan arbejde sammen, til at arbejde sammen.
  • Den Bridge mønster , for at opdele en klasse, der faktisk burde være en eller flere, til et sæt af klasser, der hører til et hierarki, som gør det muligt implementeringer, der skal udvikles uafhængigt af hinanden.
  • The Decorator mønster , for at tilføje ansvar til objekter dynamisk.

Adfærdsmæssig

Adfærdsmønstre er almindelige mønstre til at lette elegant kommunikation mellem objekter.

Eksempler på adfærdsmønstre er:

  • Den skabelon mønster , for at udskyde de nøjagtige trin i en algoritme til en underklasse.
  • Den Mediator mønster , for at definere de nøjagtige kommunikationskanaler tilladt mellem klasser.
  • Den Observer mønster , der gør det muligt klasser for at abonnere på noget af interesse, og at blive underrettet, når en ændring indtraf.

Design mønster kritik

Designmønstre er gode og alt sammen, men nogle gange kan de gøre vores designs mere komplicerede. Det er vigtigt at huske YAGNI og forsøge at holde vores designs så enkle som muligt. Brug kun designmønstre, når du er virkelig sikker på, at du har brug for dem. Du ved, hvornår du vil.

Hvis vi ved, hvad hvert af disse mønstre er, hvornår vi skal bruge dem, og hvornår vi ikke engang gider at bruge dem, er vi i god form til at begynde at forstå, hvordan man designer større systemer.

Årsagen bag det er, at arkitektoniske mønstre bare er designmønstre, der er sprængt i skala til det høje niveau , hvor designmønstre er implementeringer på lavt niveau (tættere på klasser og funktioner).

Ressourcer

Refactoring Guru - Designmønstre

Trin 6: Arkitektoniske principper

Nu er vi på et højere niveau af tænkning ud over klasseniveauet.

Vi forstår nu, at de beslutninger, vi træffer over for organisering og opbygning af relationer mellem komponenter på højt og lavt niveau, vil have en betydelig indflydelse på vedligeholdelsesevnen, fleksibiliteten og testbarheden af ​​vores projekt.

Lær de vejledende principper, der hjælper dig med at opbygge den fleksibilitet, som din codebase har brug for for at kunne reagere på nye funktioner og krav med så lidt indsats som muligt.

Her er hvad jeg vil anbefale at lære lige uden for flagermusen:

  • Principper for komponentdesign: Det stabile abstraktionsprincip, det stabile afhængighedsprincip og det acykliske afhængighedsprincip for, hvordan komponenter organiseres, deres afhængighed, hvornår de skal parres, og konsekvenserne af ved et uheld at skabe afhængighedscyklusser og stole på ustabile komponenter.
  • Politik vs. detaljer for at forstå, hvordan man adskiller reglerne i din applikation fra implementeringsoplysningerne.
  • Grænser, og hvordan man identificerer de underdomæner, som funktionerne i din applikation hører til.

Onkel Bob opdagede og oprindeligt dokumenterede mange af disse principper, så den bedste ressource til at lære om dette er igen "Ren arkitektur".

Trin 7: Arkitektoniske stilarter

Arkitektur handler om de ting, der betyder noget.

Det handler om at identificere, hvad et system har brug for for at det skal lykkes, og derefter stable oddsene for succes ved at vælge den arkitektur, der passer bedst til kravene.

For eksempel vil et system, der har meget forretningslogisk kompleksitet , drage fordel af at bruge en lagdelt arkitektur til at indkapsle denne kompleksitet.

Et system som Uber skal kunne håndtere mange realtidsbegivenheder på én gang og opdatere drivernes placeringer, så publicerings-abonnement -stilarkitektur kan være mest effektiv.

Jeg gentager mig her, fordi det er vigtigt at bemærke, at de 3 kategorier af arkitektoniske stilarter ligner de 3 kategorier af designmønstre, fordi arkitektoniske stilarter er designmønstre på højt niveau .

Strukturel

Projekter med forskellige niveauer af komponenter og bred funktionalitet vil enten have gavn af eller lide ved at vedtage en strukturel arkitektur.

Her er et par eksempler:

  • Komponentbaserede arkitekturer understreger adskillelse af bekymringer mellem de enkelte komponenter i et system. Tænk Google et øjeblik. Overvej, hvor mange applikationer de har i deres virksomhed (Google Docs, Google Drive, Google Maps osv.). For platforme med masser af funktionalitet opdeler komponentbaserede arkitekturer bekymringerne i løst koblede uafhængige komponenter. Dette er en vandret adskillelse.
  • Monolitisk betyder, at applikationen kombineres til en enkelt platform eller et program, der er implementeret helt. Bemærk: Du kan have en komponentbaseret OG monolitisk arkitektur, hvis du adskiller dine applikationer korrekt, men alligevel implementerer det hele som et stykke .
  • Lagdelte arkitekturer adskiller bekymringerne lodret ved at skære software i infrastruktur-, applikations- og domænelag.

Ren arkitektur

Et eksempel på at skære bekymringerne ved en applikation lodret ved hjælp af en lagdelt arkitektur. Læs her for at få flere oplysninger om, hvordan du gør dette.

Beskeder

Afhængigt af dit projekt kan messaging være en virkelig vigtig komponent for systemets succes. For projekter som dette bygger meddelelsesbaserede arkitekturer oven på funktionelle programmeringsprincipper og adfærdsmæssige mønstre som observatørmønsteret.

Her er et par eksempler på beskedbaserede arkitektoniske stilarter:

  • Begivenhedsdrevne arkitekturer ser alle signifikante ændringer i tilstanden som begivenheder. For eksempel inden for en vinyl-handelsapp kan et tilbuds tilstand ændre sig fra "afventende" til "accepteret", når begge parter er enige om handlen.
  • Arkitekturer med publicerings-abonnement bygger på Observer-designmønsteret ved at gøre det til den primære kommunikationsmetode mellem selve systemet, slutbrugere / klienter og andre systemer og komponenter.

Distribueret

En distribueret arkitektur betyder simpelthen, at systemets komponenter distribueres separat og fungerer ved at kommunikere via en netværksprotokol. Distribuerede systemer kan være meget effektive til skalering af kapacitet, skaleringsteam og delegering af (potentielt dyre opgaver eller) ansvar til andre komponenter.

Et par eksempler på distribuerede arkitektoniske stilarter er:

  • Klient-server arkitektur. En af de mest almindelige arkitekturer, hvor vi deler det arbejde, der skal udføres mellem klienten (præsentation) og serveren (forretningslogik).
  • Peer-to-peer- arkitekturer distribuerer applikationslagsopgaver mellem lige så privilegerede deltagere og danner et peer-to-peer-netværk.

Trin 8: Arkitektoniske mønstre

Arkitektoniske mønstre forklarer mere taktiske detaljer, hvordan man faktisk implementerer en af ​​disse arkitektoniske stilarter .

Her er et par eksempler på arkitektoniske mønstre og de stilarter, de arver fra:

  • Domain-Driven Design er en tilgang til softwareudvikling mod virkelig komplekse problemdomæner. For at DDD skal være mest succesrig, er vi nødt til at implementere en lagdelt arkitektur for at adskille bekymringerne for en domænemodel fra de infrastrurale detaljer, der får applikationen til at køre, som databaser, webservere, cacher osv.
  • Model-View Controller er sandsynligvis det mest kendte arkitektoniske mønster til udvikling af brugergrænseflade-baserede applikationer. Det fungerer ved at opdele appen i 3 komponenter: model, visning og controller. MVC er utroligt nyttigt, når du først starter, og det hjælper dig med at komme tilbage til andre arkitekturer, men der er et punkt, når vi indser, at MVC ikke er nok til problemer med masser af forretningslogik.
  • Begivenhedssourcing er en funktionel tilgang, hvor vi kun gemmer transaktionerne og aldrig staten. Hvis vi nogensinde har brug for staten, kan vi anvende alle transaktionerne fra begyndelsen af ​​tiden.

Trin 9: Virksomhedsmønstre

Ethvert arkitektonisk mønster, du vælger, vil introducere en række konstruktioner og teknisk jargon til at gøre dig bekendt med og beslutte, om det er værd at bruge eller ej.

Hvis vi tager et eksempel, som mange af os ved, i MVC , indeholder visningen al præsentationslagskode, controlleren oversætter kommandoer og forespørgsler fra visningen til anmodninger, der håndteres af modellen og returneres af controlleren .

Hvor i modellen (M) håndterer vi disse ting ?:

  • valideringslogik
  • uforanderlige regler
  • domæne begivenheder
  • brugssager
  • komplekse forespørgsler
  • og forretningslogik

Hvis vi simpelthen bruger en ORM (objekt-relationel kortlægger) som Sequelize eller TypeORM som model , bliver alle de vigtige ting, der skal overlades til fortolkning af, hvor det skal hen, og det befinder sig i et uspecificeret lag imellem (hvad skal være en rig ) -modellen og controlleren .

mvc-2

Hentet fra "3.1 - Slim (Logic-less) modeller" i solidbook.io.

Hvis der er noget, jeg hidtil har lært på min rejse, der går ud over MVC, er det, at der er en konstruktion til alt .

For hver af de ting, som MVC ikke behandler, findes der andre virksomhedsmønstre for at løse dem. For eksempel:

  • Enheder beskriver modeller, der har en identitet.
  • Værdiobjekter er modeller, der ikke har nogen identitet og kan bruges til at indkapsle valideringslogik.
  • Domænehændelser er begivenheder, der betegner en relevant forretningsbegivenhed, der forekommer og kan abonneres på fra andre komponenter.

Afhængigt af den arkitektoniske stil, du har valgt, vil der være masser af andre virksomhedsmønstre, som du kan lære for at implementere dette mønster til det fulde potentiale.

Integrationsmønstre

Når din applikation er oppe og kører, når du får flere og flere brugere, kan du støde på ydeevneproblemer. API-opkald kan tage lang tid, servere kan gå ned fra at blive overbelastet med anmodninger osv. For at løse disse problemer kan du læse om at integrere ting som meddelelseskøer eller cacher for at forbedre ydeevnen.

Dette er sandsynligvis den mest udfordrende ting: skalering, revision og ydeevne .

At designe et system til skalering kan være utroligt udfordrende. Det kræver en dyb forståelse af begrænsningerne for hver komponent inden for arkitekturen og en handlingsplan for, hvordan man mindsker stress på din arkitektur og fortsætter med at betjene anmodninger i situationer med høj trafik.

Behovet også for behovet for at kontrollere, hvad der foregår i din ansøgning. Store virksomheder skal være i stand til at udføre revisioner for at identificere potentielle sikkerhedsproblemer, forstå hvordan brugerne bruger deres applikationer og have en log over alt, hvad der nogensinde er sket.

Dette kan være udfordrende at implementere, men almindelige arkitekturer ender med at se begivenhedsbaseret og bygge på en bred vifte af software- og systemdesignkoncepter, -principper og -praksis som Event Storming, DDD, CQRS (segregering af kommandoforespørgsel) og Event Sourcing .

Jeg håber, det var nyttigt for dig!

Fortæl mig, hvis du har nogle forslag eller spørgsmål.

Skål!

Gaffel det på GitHub

Læs bogen om software design og arkitektur

Læs opskrivningen

khalilstemmler.com - Jeg underviser avanceret TypeScript & Node.js i bedste praksis for store applikationer og hvordan man skriver fleksibel, vedligeholdelig software.