Ga naar inhoud

Referentiegids

Architectuurpatronen Uitgelegd

Wat deze patronen betekenen, waarom ze ertoe doen, en hoe ze samenwerken. Geen jargon. Echte code. Duidelijke taal.

1

Domain-Driven Design (DDD)

Organiseer per bedrijfsdomein, niet per technische laag

De meeste tutorials leren je code te organiseren per technische laag: alle models in een map, alle services in een andere, alle routes in een derde. Het werkt voor kleine projecten maar valt snel uit elkaar.

Domain-Driven Design draait dit om. Je organiseert per bedrijfsconcept. Alles van planning (models, businesslogica, routes, tests) zit in een map. Alles van facturatie zit in een andere.

Laag-gebaseerd (vermijd)

models/        (50 models, 5.000 regels)
services/      (30 services, 4.000 regels)
controllers/   (40 endpoints, 3.000 regels)
schemas/       (60 schemas, 2.000 regels)

Domein-gebaseerd (voorkeur)

domains/scheduling/
  models.py      (65 regels)
  service.py     (100 regels)
  router.py      (40 regels)
  tests/         (unit + integratie)

Waarom het uitmaakt voor AI-ondersteunde ontwikkeling

Wanneer je AI naar een laag-gebaseerd project wijst, moet het 16.000 regels parsen waarvan maar 2-5% relevant is. Met domein-gebaseerde organisatie ziet AI ~1.000 regels die 100% relevant zijn. Eerste-poging succes springt van 40% naar 88%.

Kernregels

  • Geen cross-domein imports. Domein A importeert nooit de internals van domein B. Gedeelde types gaan in een shared/ directory.
  • Service-laag bezit businesslogica. Routes zijn dunne wrappers: ze ontvangen HTTP, roepen de service aan, retourneren een response.
  • Events voor cross-domein communicatie. Als planning facturatie moet notificeren, publiceert het een event, geen directe import.

Verdieping: Les 5: Domeingrenzen


2

Event-Driven Architectuur

Domeinen communiceren via events, niet directe aanroepen

Stel je voor dat je een gebruiker deactiveert. Je moet het abonnement opzeggen, een afscheidsmail sturen, permissies intrekken, audit trail loggen, analytics bijwerken. In een traditionele codebase roept je UserService alle vijf services direct aan. Acht dependencies. Acht dingen om te mocken in tests.

Met events publiceert de UserService een feit: "een gebruiker is gedeactiveerd." Het weet niet wie er luistert, en dat hoeft ook niet. Elke subscriber handelt zijn eigen logica onafhankelijk af.

Directe aanroepen (strakke koppeling)

class UserService:
    def __init__(self, db, notifications,
        audit, subscriptions, scheduling,
        analytics, cache, permissions):
        # 8 dependencies

Event-driven (losse koppeling)

class UserService:
    def __init__(self, db, event_bus):
        # 2 dependencies

    async def deactivate(self, user_id):
        await self.event_bus.publish(
            UserDeactivated(user_id=user_id)
        )

Je hebt geen Kafka nodig

Een simpele in-process event bus (minder dan 50 regels) is genoeg voor de meeste applicaties. Dedicated message queues zijn pas nodig wanneer events een procesherstart moeten overleven of servicegrenzen moeten oversteken.

Kernregels

  • Events zijn verleden tijd. UserDeactivated, InvoiceCreated, OrderShipped. Ze beschrijven wat er gebeurd is, niet wat er moet gebeuren.
  • Events dragen IDs, geen objecten. Houd payloads minimaal. Subscribers halen op wat ze nodig hebben.
  • Subscribers beinvloeden de publisher niet. Als een subscriber faalt, slaagt de originele actie nog steeds. Handel fouten af in de subscriber.

Verdieping: Les 6: Events Over Calls


3

Monoliet-eerst

Begin simpel, extraheer services wanneer je bewijs hebt

"Moet ik microservices gebruiken?" is de verkeerde eerste vraag. De juiste vraag is: "Heb ik bewijs dat een domein onafhankelijk moet schalen?" Als het antwoord nee is (en voor de meeste vroege projecten is dat zo), begin dan met een monoliet.

Een goed gestructureerde monoliet met domeingrenzen geeft je 90% van de voordelen van microservices met een fractie van de operationele complexiteit. Geen service mesh. Geen distributed tracing hoofdpijn. Geen "welke service faalt om 3 uur 's nachts" puzzels.

Wanneer een service extraheren

Extraheer een domein naar een aparte service alleen wanneer je concreet bewijs hebt van een van deze:

Onafhankelijk schalen

Het domein verwerkt 100x meer load dan de rest van de applicatie.

Ander release-tempo

Het domein moet onafhankelijk deployen, meerdere keren per dag.

Andere tech stack

Het domein vereist een taal of framework dat de monoliet niet gebruikt.

Kernregels

  • API-first binnen de monoliet. Domeinen communiceren via gedefinieerde interfaces, ook binnen een proces. Dit maakt toekomstige extractie eenvoudig.
  • Domeingrenzen zijn de voorbereiding. Als je DDD en event-driven communicatie volgt, wordt het extraheren van een domein een deployment-wijziging, geen herschrijving.
  • Een repo, meerdere domeinen. Houd alles in een monorepo. Splits per domeinmap, niet per repository.

Gerelateerd: Les 5: Domeingrenzen


4

Row-Level Security (RLS)

Multi-tenant isolatie afgedwongen door de database

In een multi-tenant applicatie delen meerdere klanten (tenants) dezelfde database. De cruciale vraag: hoe zorg je dat Tenant A nooit de data van Tenant B ziet?

De meeste applicaties lossen dit op met applicatie-level filters, zoals WHERE tenant_id = X toevoegen aan elke query. Maar een gemist filter, een vergeten check, een developer die er niet van weet, en je hebt een datalek.

Row-Level Security is een PostgreSQL-functie die deze check naar de database zelf verplaatst. Het filter is automatisch en kan niet omzeild worden door applicatiecode.

Hoe het werkt

-- 1. RLS inschakelen op de tabel
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;

-- 2. Policy aanmaken: filter op huidige tenant
CREATE POLICY tenant_isolation ON projects
  FOR ALL TO app_user
  USING (
    tenant_id = current_setting('app.current_tenant_id')::uuid
  );

-- 3. Context instellen in je middleware (elk request)
SET app.current_tenant_id = 'tenant-uuid-hier';
SET ROLE app_user;

-- Elke query wordt nu automatisch gefilterd.
-- SELECT * FROM projects retourneert alleen data van deze tenant.

De fail-safe standaard

Wat gebeurt er als de sessievariabele niet is ingesteld? Met RLS is het antwoord: nul rijen terug. Geen context, geen data. Dit is het tegenovergestelde van applicatie-level checks waar de standaard is "retourneer alles tenzij iets het stopt."

Kernregels

  • Afdwingen op de database, niet de applicatie. Applicatiebugs kunnen database-level policies niet omzeilen.
  • Fail closed. Als sessievariabelen niet zijn ingesteld, retourneert de policy nul rijen, niet alle rijen.
  • Test isolatie in CI. Maak twee tenants aan, voeg data toe voor beide, verifieer dat tenant A de data van tenant B niet kan opvragen.
  • Gebruik CASE statements in policies om lege sessievariabelen veilig af te handelen zonder UUID casting fouten.

Verdieping: Les 7: Beveiliging door Architectuur


5

Kwaliteitspoorten

Drie geautomatiseerde lagen die 87% van bugs vangen

Een bug fixen bij commit kost 2 minuten. Dezelfde bug fixen in productie kost 40+ uur. Dat is een 1.200x kostenverschil. Quality gates zijn geautomatiseerde checkpoints die problemen zo vroeg mogelijk vangen.

Laag 1 < 5 seconden

Pre-commit

Opmaak, linting, secret detectie. Draait bij elke save.

Laag 2 < 60 seconden

Pre-push

Type checking, unit tests, security lint. Draait voordat code de remote bereikt.

Laag 3 < 5 minuten

CI/CD

Volledige testsuite, coverage (80%+), dependency audit. Draait op elke pull request.

Snelheidsbudgetten zijn niet onderhandelbaar

Het moment dat een kwaliteitscheck traag is, omzeilen developers het. Pre-commit hooks die 30 seconden duren? Developers voegen --no-verify toe aan hun spiergeheugen. Strikte snelheidsbudgetten houden het systeem eerlijk.

Kernregels

  • Elke laag heeft een snelheidsbudget. Pre-commit: onder 5s. Pre-push: onder 60s. CI: onder 5 minuten.
  • Fail fast. Stop bij de eerste fout. Draai geen trage checks als snelle checks al gefaald hebben.
  • Secret detectie is niet optioneel. Een gelekte API key vangen voor commit kost 0 moeite. Na push kost het een rotatie en een audit.

Verdieping: Les 8: Kwaliteitspoorten · Configs ophalen: Patroon Composer


6

Type Safety Pipeline

Een schema, geen drift, geen runtime typefouten

Je backend definieert een API. Je frontend consumeert het. Daartussen zit een gat waar types kunnen driften. Je hernoemt een veld in Python, maar de TypeScript client gebruikt nog de oude naam. Het compileert. Het deployt. Dan breekt het om 2 uur 's nachts.

Een type safety pipeline elimineert dit gat. Een schemadefinitie, automatisch doorgegeven aan alle consumers.

De pipeline

Pydantic / Zod OpenAPI spec TypeScript types

Wijzig een veld in het backend schema. Draai een commando. Frontend types updaten automatisch. TypeScript compiler vangt elke gebroken referentie bij build time, niet runtime.

Kernregels

  • Schrijf nooit handmatig API client types. Genereer ze vanuit de OpenAPI spec. Handmatige typedefinities driften binnen weken.
  • Verbied any. Zet strict: true aan in tsconfig. Elke any is een gat in je type safety net.
  • Draai de pipeline in CI. Als gegenereerde types afwijken van gecommitte types, faalt de build. Niemand shipt verouderde types.

Verdieping: Les 4: Type Safety Pipeline


Hoe deze patronen samenwerken

Domeingrenzen geven AI gerichte context (1.000 regels in plaats van 16.000).

Events houden domeinen onafhankelijk. Geen circulaire imports, geen 8-dependency constructors.

Type safety zorgt dat wijzigingen zonder drift propageren, van backend naar frontend.

RLS dwingt beveiliging af op het fundament zodat je snel kunt bouwen zonder angst.

Quality gates vangen 87% van bugs automatisch voordat een mens code reviewt.

Een monoliet-eerst aanpak houdt operationele complexiteit laag terwijl je bouwt.

Elk patroon versterkt de andere. Hoe meer je adopteert, hoe sneller en veiliger je ontwikkeling wordt.

Veelgestelde vragen

Wat is Domain-Driven Design (DDD) in simpele woorden?
DDD betekent dat je je code organiseert rond bedrijfsconcepten in plaats van technische lagen. In plaats van alle models in een map en alle services in een andere, groepeer je alles van "planning" of "facturatie" samen. Elke domeinmap bevat zijn eigen models, logica, routes en tests, waardoor het zelfstandig en begrijpelijk is voor zowel mensen als AI.
Heb ik microservices nodig voor Domain-Driven Design?
Nee. DDD werkt perfect binnen een monoliet. Je organiseert per domein binnen een enkele codebase. Microservices zijn een infrastructuurbeslissing die je later neemt, alleen wanneer een specifiek domein onafhankelijk moet schalen of een andere tech stack nodig heeft.
Wat is event-driven architectuur en wanneer gebruik ik het?
Event-driven architectuur betekent dat domeinen communiceren door events te publiceren ("dit is gebeurd") in plaats van elkaar direct aan te roepen. Gebruik het wanneer een actie meerdere neveneffecten triggert, zoals een gebruikersregistratie die een welkomstmail moet sturen, een facturatierecord moet aanmaken en analytics moet loggen. Events laten je nieuwe neveneffecten toevoegen zonder de originele code aan te passen.
Heb ik Kafka of RabbitMQ nodig voor event-driven architectuur?
Niet voor de meeste applicaties. Een simpele in-process event bus (minder dan 50 regels code) handelt de overgrote meerderheid van use cases af. Je hebt pas dedicated message queues nodig wanneer events een procesherstart moeten overleven, of wanneer verschillende domeinen als aparte services draaien.
Wat is Row-Level Security (RLS) en waarom is het belangrijk?
RLS is een PostgreSQL-functie die databasequeries automatisch filtert op basis van de huidige gebruiker of tenant. In plaats van "WHERE tenant_id = X" aan elke query in je applicatiecode toe te voegen, dwingt de database het voor je af. Zelfs als je applicatie een bug heeft, zijn data van andere tenants onzichtbaar. Het is de sterkste vorm van multi-tenant data-isolatie.
Wat is de monoliet-eerst aanpak?
Begin met een goed gestructureerde monoliet in plaats van direct naar microservices te springen. Houd al je code in een repository, organiseer per domein, en extraheer een domein pas naar een aparte service wanneer je concreet bewijs hebt dat het onafhankelijk moet schalen. Dit geeft je 90% van de voordelen van microservices met een fractie van de operationele complexiteit.
Wat zijn quality gates en hoe vangen ze automatisch bugs?
Quality gates zijn drie lagen geautomatiseerde checks: pre-commit hooks (onder 5 seconden, vangen opmaak en secrets), pre-push hooks (onder 60 seconden, vangen typefouten en falende tests), en CI/CD pipelines (onder 5 minuten, draaien volledige testsuites met coverage). Samen vangen ze 87% van bugs voordat een mens code reviewt.
Hoe werken deze patronen samen?
Ze vormen een samengesteld systeem. Domeingrenzen geven AI gerichte context. Events houden domeinen onafhankelijk. RLS dwingt beveiliging af op het fundament. Quality gates vangen fouten automatisch. Elk patroon versterkt de andere. Hoe meer je adopteert, hoe sneller en veiliger je ontwikkeling wordt.

Klaar om deze patronen toe te passen?

Begin de gratis cursus