Uge 44 – Implementering af Kafka

Portfolio: Marlen Halvorsen

Uge 44 – Implementering af Kafka

Dato: 01-11-2025  |  Uge: 44

Kontekst

Proces og læringsmålBackend-udvikling og API-design

Mål

Implementere en Kafka-adapter, der kan lytte til et topic fra IngestionService.
Implementere en Kafka-adapter, der kan publicere events på et nyt, dedikeret topic.
Skrive UnitTests, der kan bidrage til at kvalitetssikre driften af microservicen.

Proces

Undersøge dokumentation af implementation af Kafka i C# ved .NET Framework.
Overveje arkitekturmæssige beslutninger, herunder revidere tidligere HLD og LLD-modeller for inklusion af message broker.
Praktisk implementering af LLD. Udførlig UnitTesting af hhv. Producer, Consumer og KafkaSerializer.
Overvejelser og betragtninger undervejs og bagefter.

Resultat

High Level Design
Product Owner arbejder selv med message brokeren Kafka, hvorfor message brokeren vil omtales som Kafka i dette indlæg.
Det gøres opmærksom på, at Use Case er opdateret med et message broker-agnostisk sprog, således at der kan skiftes til andre message brokers, hvis det skulle være ønskeligt, uden at rette i de tilhørende modeller i HLD.
Der gøres også opmærksom på, at der arbejdes sideløbende med integration af LLM for berigelse af metadata, hvorfor dette også figurerer i Use Casen.
For tidligere iteration af HLD og LLD se her: Uge 38.

Use Case
Event-baseret billedklassificering

Man kan se i Use Casen, at der nu omtales Message Broker, der sætter i gang en intern proces, hvorefter det afsluttes med at publicere et resultat.

Operations kontrakt
Operationskontrakt

Her ses en opdateret operationskontrakt for at tydeliggøre Pre- og PostConditions.

SystemSekvensDiagram
Systemsekvensdiagram

Diagrammet viser, hvordan et eksternt event trigger den interne logik i microservicen.
Når analysen er færdig, publiceres et nyt event som svar.

Domænemodel
Domænemodel

Billedet illustrerer domænemodellen, hvor de centrale objekter og deres attributter indgår.
Det fremgår, at ProviderResult (som indeholder rå JSON og billedreference) ikke er inkluderet, da disse data kun anvendes midlertidigt til videre behandling og ikke gemmes.
Modellen viser, hvordan resultatet af billedklassificering samles i et RecognitionSummary, og hvordan et KlassificeringEvent udarbejdes og publiceres.

Low Level Design
Overvejelser: Der implementeres en Kafka-adapter, som kan lytte på et topic fra IngestionService, samt en Kafka-adapter, der kan publicere events på et topic, når analysen inde i servicen er færdig.
Adapterne indgår i Ports and Adapters-arkitekturen og placeres derfor i Infrastructure-laget – konkret i en ny undermappe kaldet Kafka under Adapters.

For at håndtere flowet KafkaEvent → MicroService → KafkaEvent vil der være en indadgående port i Application-laget, som orkestrerer kald mellem RecognitionService og de relevante Kafka-adaptere.
Dette design muliggør uafhængig test af producer og consumer og sikrer Separation of Concerns.

Der implementeres også en Serializer, som håndterer konvertering mellem event og intern DTO (event → dto og dto → event).
Det sikrer et applikationslag, der er agnostisk over for message broker-teknologien og ikke kender til de underliggende transportdetaljer.

SekvensDiagram
Sekvensdiagram – Implementering af Kafka

Ovenfor vises, hvordan objekterne kommunikerer over tid i en sekventiel rækkefølge.
Det fremgår, at microservicen aktiveres ved at lytte efter events på et specifikt Kafka-topic.
Når en event modtages, igangsættes en række processer, som afsluttes med, at der publiceres en ny event på et andet Kafka-topic.

Dependency Inversion og konfiguration for at fremme Unit Testing
Alle adaptere og services (fx KafkaProducer, KafkaConsumer, samt konfiguration og serialisering) registreres via Dependency Injection i Program.cs.

Application-laget afhænger kun af interfaces og abstraherede services, hvilket gør det muligt at teste orkestreringen uafhængigt af infrastruktur.
Det muliggør både mocking i tests og udskiftning af message broker-teknologi uden ændringer i applikationslaget.

Ved at holde oprettelse og konfiguration af afhængigheder samlet ét sted opretholdes en klar arkitektur med Separation of Concerns og kontrol over instanslivscyklus.

Test af PublishAsync

Herover ses en enhedstest af PublishAsync-metoden fra RecognitionCompletedKafkaProducer.
Den er sat op ved brug af Arrange, Act og Assert-struktur, hvor billedet viser til de to sidste dele.
Testen verificerer følgende:

  • At serialiseringen af eventet udføres præcis én gang
  • At ProduceAsync kaldes med den forventede nøgle og payload
  • At der logges information om publiceringen én gang

Denne test sikrer, at producenten fungerer korrekt og opfører sig deterministisk ift. at sende events til Kafka.

Note:
I uge 36 implementerede jeg den første udgave af min Hexagonal Architecture med HTTP som inbound adapter
(se indlægget her).
Med Kafka-integration i uge 44 har jeg udvidet arkitekturen med en ny inbound adapter (Kafka-consumer), som stadig kalder ind i application-laget via en port (IRecognitionRequestedHandler).
Det viser, hvordan servicegrænser og ansvar fastholdes, selv når transportlaget skiftes.
Hexagonen ændrer sig ikke, kun adapterne gør.

Afsluttende overvejelser
Det har været meget værdifuldt at arbejde med både arkitektur og implementering i sammenhæng.
Erfaringen med at skrive adaptere og sikre testbarhed er direkte anvendelig i fremtidige microservices.
Jeg trives godt i den iterative proces, hvor jeg skiftevis tilegner mig teori og arbejder praktisk med implementering.
Det giver mig mulighed for hele tiden at bygge videre på den viden, jeg har opnået – særligt gennem Feed-forward-delen af processen.

Videre plan

Uge 44 og 45: Fortsat implementering af LLM ved at refaktorere HLD og LLD, samt unit testing af denne del.

Ressourcer