Elke dag, elk moment tijdens onze ingenieurscarrière, komen we veel verschillende problemen van verschillende complexiteit en situaties tegen waarin we een beslissing moeten nemen of deze moeten uitstellen vanwege een gebrek aan gegevens. Wanneer we nieuwe services bouwen, infrastructuur construeren of zelfs ontwikkelingsprocessen vormen, raken we een enorme wereld van verschillende uitdagingen aan.
Het is een uitdaging, en misschien zelfs onmogelijk, om alle problemen op te sommen. Sommige van deze problemen kom je alleen tegen als je in een specifieke niche werkt. Aan de andere kant zijn er veel problemen die we allemaal moeten begrijpen om op te lossen, omdat ze cruciaal zijn voor het bouwen van IT-systemen. Met een grote waarschijnlijkheid kom je ze in alle projecten tegen.
In dit artikel deel ik mijn ervaringen met een aantal problemen die ik ben tegengekomen bij het maken van softwareprogramma's.
Als we op Wikipedia kijken, vinden we de volgende definitie
Bij aspect-georiënteerde softwareontwikkeling zijn cross-cutting concerns aspecten van een programma die meerdere modules beïnvloeden, zonder de mogelijkheid om in een van hen te worden ingekapseld. Deze concerns kunnen vaak niet netjes worden ontbonden van de rest van het systeem in zowel het ontwerp als de implementatie, en kunnen resulteren in ofwel scattering (code duplicatie), tangled (significante afhankelijkheden tussen systemen), of beide.
Het beschrijft heel goed wat het is, maar ik wil het graag wat uitbreiden en vereenvoudigen:
Een cross-cutting concern is een concept of onderdeel van het systeem/de organisatie dat invloed heeft op (of 'doorsnijdt') veel andere onderdelen.
De beste voorbeelden van dergelijke zorgen zijn systeemarchitectuur, logging, beveiliging, transactiebeheer, telemetrie, databaseontwerp en er zijn er nog veel meer. We gaan er later in dit artikel uitgebreid op in.
Op codeniveau worden cross-cutting concerns vaak geïmplementeerd met behulp van technieken zoals Aspect-Oriented Programming (AOP) , waarbij deze concerns worden gemodulariseerd in afzonderlijke componenten die in de hele applicatie kunnen worden toegepast. Dit houdt de bedrijfslogica geïsoleerd van deze concerns, waardoor de code leesbaarder en beter te onderhouden is.
Er zijn veel mogelijke manieren om aspecten te classificeren door ze te segmenteren met verschillende eigenschappen zoals scope, grootte, functionaliteit, belang, doel en andere, maar in dit artikel ga ik een eenvoudige scope-classificatie gebruiken. Hiermee bedoel ik waar dit specifieke aspect naartoe gaat, of het nu de hele organisatie is, een bepaald systeem of een specifiek element van dat systeem.
Ik ga de aspecten dus opsplitsen in Macro en Micro .
Met macro- aspect bedoel ik vooral de overwegingen die we volgen voor het hele systeem, zoals de gekozen systeemarchitectuur en het ontwerp ervan (monolithisch, microservices, servicegerichte architectuur), de technologie-stack, de organisatiestructuur, enzovoort. Macro- aspecten hebben vooral betrekking op strategische en hoogwaardige beslissingen.
Ondertussen ligt het Micro- aspect veel dichter bij het codeniveau en de ontwikkeling. Bijvoorbeeld, welk framework wordt gebruikt voor interactie met de database, de projectstructuur van mappen en klassen, of zelfs specifieke objectontwerppatronen.
Hoewel deze indeling niet ideaal is, helpt het om inzicht te krijgen in mogelijke problemen en het belang en de impact van de oplossingen die we daarvoor toepassen.
In dit artikel zal ik mij vooral richten op de macro-aspecten.
Toen ik net begon met leren over softwarearchitectuur, las ik veel interessante artikelen over de wet van Conway en de impact ervan op de organisatiestructuur. Vooral deze . Deze wet stelt dat
Elke organisatie die een systeem (in brede zin) ontwerpt, zal een ontwerp produceren waarvan de structuur een kopie is van de communicatiestructuur van de organisatie.
Ik heb altijd geloofd dat dit concept inderdaad heel universeel is en de Gouden Regel vertegenwoordigt.
Toen begon ik de Domain-Driven Design (DDD)-benadering van Eric Evans te leren voor het modelleren van systemen. Eric Evans benadrukt het belang van Bounded Context-identificatie. Dit concept omvat het verdelen van een complex domeinmodel in kleinere, beter beheersbare secties, elk met zijn eigen beperkte set aan kennis. Deze benadering helpt bij effectieve teamcommunicatie, omdat het de noodzaak van uitgebreide kennis van het hele domein vermindert en contextswitching minimaliseert, waardoor gesprekken efficiënter worden. Contextswitching is het ergste en meest resource-intensieve ooit. Zelfs computers hebben er moeite mee. Hoewel het onwaarschijnlijk is dat het helemaal niet mogelijk is om contextswitching te bereiken, denk ik dat we daar naar moeten streven.
Terugkomend op de Wet van Conway: ik heb er een aantal problemen mee gevonden.
Het eerste probleem dat ik ben tegengekomen met de wet van Conway, die suggereert dat systeemontwerp de organisatiestructuur weerspiegelt, is de mogelijkheid om complexe en uitgebreide Bounded Contexts te vormen. Deze complexiteit ontstaat wanneer de organisatiestructuur niet is afgestemd op domeingrenzen, wat leidt tot Bounded Contexts die sterk onderling afhankelijk zijn en vol zitten met informatie. Het leidt tot frequente contextwisselingen voor het ontwikkelteam.
Een ander probleem is dat organisatorische terminologie doorlekt naar het codeniveau. Wanneer organisatiestructuren veranderen, zijn codebase-aanpassingen noodzakelijk, wat waardevolle resources verbruikt.
Dus, het volgen van de Inverse Conway Manoeuvre helpt om het systeem en de organisatie te bouwen die de gewenste softwarearchitectuur aanmoedigen. Het is echter opmerkelijk om te zeggen dat deze aanpak niet erg goed zal werken in reeds gevormde architectuur en structuren, aangezien veranderingen in dit stadium langdurig zijn, maar het presteert uitzonderlijk goed in startups, aangezien zij snel veranderingen doorvoeren.
Dit patroon of “anti-patroon” drijft het bouwen van een systeem zonder enige architectuur. Er zijn geen regels, geen grenzen en geen strategie om de onvermijdelijke groeiende complexiteit te beheersen. Complexiteit is de meest geduchte vijand in de reis van het bouwen van softwaresystemen.
Om te voorkomen dat we een dergelijk systeem bouwen, moeten we ons aan specifieke regels en beperkingen houden.
Er zijn talloze definities voor softwarearchitectuur. Ik vind er veel leuk, omdat ze verschillende aspecten ervan bestrijken. Om echter over architectuur te kunnen redeneren, moeten we er natuurlijk een aantal in ons hoofd vormen. En het is opmerkelijk om te zeggen dat deze definitie kan evolueren. Dus, althans voor nu, heb ik de volgende beschrijving voor mezelf.
Bij softwarearchitectuur gaat het om de beslissingen en keuzes die u dagelijks maakt en die van invloed zijn op het gebouwde systeem.
Om beslissingen te nemen moet je principes en patronen in je "tas" hebben om opkomende problemen op te lossen, het is ook essentieel om te stellen dat het begrijpen van de vereisten de sleutel is tot het bouwen van wat een bedrijf nodig heeft. Soms zijn vereisten echter niet transparant of zelfs niet gedefinieerd, in dat geval is het beter om te wachten tot er meer duidelijkheid is of te vertrouwen op je ervaring en je intuïtie. Maar hoe dan ook, je kunt geen goede beslissingen nemen als je geen principes en patronen hebt om op te vertrouwen. Dat is waar ik kom tot de definitie van Software Architecture Style.
De softwarearchitectuurstijl is een reeks principes en patronen die bepalen hoe software gebouwd moet worden.
Er zijn veel verschillende architectuurstijlen die zich op verschillende aspecten van de geplande architectuur richten. Het is dan ook normaal dat er meerdere van deze stijlen tegelijk worden toegepast.
Bijvoorbeeld, zoals:
Monolithische architectuur
Domeingestuurd ontwerp
Component-gebaseerd
Microdiensten
Pijp en filters
Gebeurtenisgestuurd
Microkernel
Servicegericht
enzovoort…
Natuurlijk hebben ze hun voor- en nadelen, maar het belangrijkste dat ik heb geleerd, is dat architectuur geleidelijk evolueert, afhankelijk van daadwerkelijke problemen. Beginnen met de monolithische architectuur is een geweldige keuze om operationele complexiteit te verminderen. Het is zeer waarschijnlijk dat deze architectuur aan uw behoeften voldoet, zelfs nadat u de Product-market Fit (PMI)-fase van het bouwen van het product hebt bereikt. Op schaal kunt u overwegen om over te stappen op een event-driven benadering en microservices om onafhankelijke implementatie, heterogene tech stack-omgeving en minder gekoppelde architectuur te bereiken (en in de tussentijd minder transparant vanwege de aard van event-driven en pub-sub-benaderingen als deze worden aangenomen). Eenvoud en efficiëntie liggen dicht bij elkaar en hebben een grote impact op elkaar. Meestal hebben gecompliceerde architecturen invloed op de ontwikkelingssnelheid van nieuwe functies, het ondersteunen en onderhouden van bestaande functies en het uitdagen van de natuurlijke evolutie van het systeem.
Complexe systemen vereisen echter vaak een complexe en uitgebreide architectuur, wat onvermijdelijk is.
Eerlijk gezegd is dit een heel erg breed onderwerp, en er zijn veel geweldige ideeën over hoe je systemen voor natuurlijke evolutie kunt structureren en bouwen. Op basis van mijn ervaring heb ik de volgende aanpak uitgewerkt:
Het is ook van belang om de cijfers en statistieken zoals DAU (Daily Active Users), MAU (Monthly Active Users), RPC (Request Per Second) en TPC (Transaction Per Second) te begrijpen. Dit kan u helpen bij het maken van keuzes, omdat de architectuur voor 100 actieve gebruikers en 100 miljoen actieve gebruikers verschilt.
Als laatste opmerking zou ik willen zeggen dat architectuur een significante impact heeft op het succes van het product. Slecht ontworpen architectuur voor de producten is vereist bij het schalen, wat zeer waarschijnlijk leidt tot mislukking, aangezien klanten niet zullen wachten terwijl u het systeem schaalt, ze zullen een concurrent kiezen, dus we moeten potentiële schaalvergroting voor zijn. Hoewel ik toegeef dat het soms geen lean-aanpak kan zijn, is het idee om een schaalbaar maar nog niet geschaald systeem te hebben. Aan de andere kant kost het hebben van een zeer gecompliceerd en al geschaald systeem zonder klanten of plannen om er veel van te krijgen, u geld voor uw bedrijf voor niets.
Het selecteren van een technologiestack is ook een beslissing op macroniveau, aangezien deze van invloed is op het aannemen van personeel, de perspectieven op de natuurlijke evolutie van het systeem, de schaalbaarheid en de systeemprestaties.
Dit is de lijst met basisoverwegingen voor het kiezen van een technologiestack:
Hoe kan het combineren van meerdere technologiestacks de bedrijfsgroei beïnvloeden?
Vanuit één perspectief zou het introduceren van nog een stack uw werving kunnen opschalen, maar aan de andere kant brengt het extra onderhoudskosten met zich mee, omdat u beide stacks moet ondersteunen. Dus, zoals ik eerder zei, zou in mijn optiek alleen extra behoefte een argument moeten zijn om meer technologiestacks te integreren.
Maar hoe zit het met het principe van het selecteren van het beste gereedschap voor een specifiek probleem?
Soms heb je geen andere keus dan nieuwe hulpmiddelen aan te schaffen om een specifiek probleem op te lossen op basis van dezelfde overwegingen als hierboven genoemd. In zulke gevallen is het zinvol om de beste oplossing te selecteren.
Het creëren van systemen zonder hoge koppeling aan een specifieke technologie kan een uitdaging zijn. Toch is het nuttig om te streven naar een conditie waarin het systeem niet nauw gekoppeld is aan technologie, en het zal niet sterven als morgen een specifiek framework of tool kwetsbaar of zelfs verouderd wordt.
Een andere belangrijke overweging heeft betrekking op open-source en propriëtaire software-afhankelijkheden. Propriëtaire software biedt u minder flexibiliteit en de mogelijkheid om te worden aangepast. Toch is de gevaarlijkste factor vendor lock-in, waarbij u afhankelijk wordt van de producten, prijzen, voorwaarden en roadmap van een leverancier. Dit kan riskant zijn als de leverancier van richting verandert, de prijzen verhoogt of het product stopzet. Open-source software vermindert dit risico, omdat een enkele entiteit het niet controleert. Het elimineren van een enkelvoudig punt van falen op alle niveaus is een sleutel tot het bouwen van betrouwbare systemen voor groei.
Een single point of failure (SPOF) verwijst naar elk onderdeel van een systeem dat, als het faalt, ervoor zorgt dat het hele systeem stopt met functioneren. Het elimineren van SPOF's op alle niveaus is cruciaal voor elk systeem dat hoge beschikbaarheid vereist. Alles, inclusief kennis, personeel, systeemcomponenten, cloudproviders en internetkabels, kan falen.
Er zijn verschillende basistechnieken die we kunnen toepassen om 'single points of failure' te elimineren:
In dit artikel bespreken we verschillende belangrijke macro- aspecten en hoe we met de complexiteit ervan kunnen omgaan.
Bedankt voor het lezen! Tot de volgende keer!