paint-brush
En tabel som API? Illusioner og virkelighedved@truewebber
Ny historie

En tabel som API? Illusioner og virkelighed

ved Aleksei Kish9m2025/03/05
Read on Terminal Reader

For langt; At læse

Forfatteren hævder, at brugen af en delt databasetabel som et middel til service-til-service kommunikation er et anti-mønster. Selvom det kan se ud til at være en hurtig løsning, fører det til versioneringshovedpine, uklart ejerskab og vanskeligheder med skalerbarhed og sikkerhed. I stedet fortaler artiklen for en "Contract First"-tilgang, hvor hver tjeneste formelt definerer sine grænseflader og bevarer ejerskabet af sine egne data. Denne metode fremmer klarere ansvarlighed, jævnere udvikling og mere robust integration på tværs af teams.
featured image - En tabel som API? Illusioner og virkelighed
Aleksei Kish HackerNoon profile picture
0-item
1-item

Indledning

Hvad er en "kontrakt" i forbindelse med serviceinteraktion?

I forbindelse med interagerende servicemoduler opstår det uundgåelige spørgsmål: Efter hvilke regler foregår kommunikationen? I IT-produkter repræsenterer en "kontrakt" en formel forståelse af, hvilke data der flyder mellem systemer, og hvordan de transmitteres. Dette indebærer dataformatet (JSON, Protobuf osv.), strukturelle elementer (felter, datatyper), kommunikationsprotokol (REST, gRPC, beskedkøer) og andre specifikationer.


En kontrakt sikrer åbenhed (alle ved, hvad der modtages og sendes), forudsigelighed (vi kan opdatere kontrakten og vedligeholde versioner) og pålidelighed (vores system fejler ikke, hvis vi laver velstyrede ændringer).

Hvorfor folk har en tendens til at vælge en tabel i databasen som en "kontrakt".

I praksis, selvom alle taler om mikrotjenester, "kontrakter" og API'er, ser vi ofte, at folk anvender tilgangen: "Hvorfor ikke oprette en delt tabel i databasen i stedet for at bygge API'er?"


  • Historisk eller organisatorisk vane: Når alt altid har været gemt i ét DB-system i én virksomhed, hvorfor så genopfinde hjulet?


  • "Quick fix"-mentaliteten: Vi skriver, du vil læse uden at opsætte autorisationsregler og designe API-specifikationer.


  • "Big data"-argumentet: Når du arbejder med ti eller endda hundredvis af gigabyte data, virker direkte overførsel til en delt tabel enklere, hurtigere og mere økonomisk, men i praksis skaber det problemer med skalerbarhed og ydeevne samt problemer med dataejerskab.


Derfor kan det virke effektivt og optimeret til hurtige resultater at bruge en delt tabel til dataudveksling, men det genererer forskellige tekniske og organisatoriske udfordringer i det lange løb. Men når teams vælger delte tabeller til dataudveksling, kan de stå over for adskillige problemer under implementeringen.

Hvorfor "en tabel i databasen" ikke er en kontrakt (og hvorfor det er et anti-mønster).

Mangel på en klart defineret grænseflade

Når tjenester kommunikerer via REST/gRPC/GraphQL, har de en formel definition: OpenAPI (Swagger), protobuf-skemaer eller GraphQL-skemaer. Disse definerer i detaljer, hvilke ressourcer (endepunkter) der er tilgængelige, hvilke felter der forventes, deres typer og anmodnings-/svarformaterne. Når 'et delt bord' fungerer som en kontrakt, er der ikke en formel beskrivelse: Der er ingen formel beskrivelse af kontrakten; kun tabelskemaet (DDL) er tilgængeligt, og selv det er ikke veldokumenteret. Enhver mindre ændring af tabelstrukturen (f.eks. tilføjelse eller sletning af en kolonne, ændring af datatyper) kan påvirke andre hold, der læser fra eller skriver til denne tabel.

Versions- og evolutionsproblemer

API-versionering er en normal praksis: Vi har muligvis v1, v2 og så videre, og vi kan bevare bagudkompatibilitet og derefter gradvist flytte klienter til de nyere versioner. For databasetabeller har vi kun DDL-operationer (f.eks. ALTER TABLE ), som er tæt koblet til en specifik DB-motor og kræver omhyggelig håndtering af migreringer.


Der er intet centraliseret system, der kan sende advarsler til forbrugere om skemaændringer, der kræver, at de opdaterer deres forespørgsler. Som følge heraf kan der forekomme "under-bordet"-aftaler : Nogen kan skrive i en chat, "I morgen ændrer vi kolonne X til Y", men der er ingen garanti for, at alle er klar i tide.

Intet klart ejerskab

Når der er en klart defineret API, er det tydeligt, hvem der ejer den: tjenesten, der fungerer som API-udgiver. Når flere teams bruger den samme databasetabel, er der forvirring om, hvem der skal bestemme strukturen og hvilke felter, der skal lagres, og hvordan de skal fortolkes. Som et resultat kan bordet blive "ingens ejendom", og hver ændring bliver en søgen: "Vi er nødt til at tjekke med det andet hold, hvis de bruger den gamle kolonne!"

Sikkerheds- og adgangskontrolproblemer

Det er svært at holde styr på, hvem der kan læse og skrive til en tabel, hvis mange hold har adgang til DB. Der er en chance for, at uautoriserede tjenester kan få adgang til dataene, selvom det ikke var beregnet til dem. Det er nemmere at håndtere sådanne problemer med en API: Du kan kontrollere adgangsrettighederne (hvem kan kalde hvilke metoder), bruge godkendelse og autorisation og overvåge, hvem der har kaldt hvad. Med et bord er det meget mere kompliceret.

Afhængighed af indre struktur

Alle interne ændringer af dataene (omorganisering af indekser, partitionering af tabellen, ændring af DB) bliver et globalt problem. Hvis tabellen fungerer som en offentlig grænseflade, kan ejeren ikke foretage interne ændringer uden at bringe alle eksterne læsere og skribenter i fare.

Smertepunkter og typiske problemer i praksis

Koordinering af ændringer

Dette er det mest smertefulde aspekt: Hvordan går man om at informere et andet team om, at skemaet vil ændre sig næste dag?

  • Et vellykket scenarie for opdatering af tabelversionen: Ejeren opretter en ny tabel med et opdateret skema parallelt med det gamle. Den gamle version forbliver tilgængelig for nuværende forbrugere, og ejeren sender dem en besked, der siger: "Den nye struktur er tilgængelig; tjek dokumentationen og deadlines. Migrér venligst, mens begge versioner eksisterer."


  • Men i et OLAP-scenarie eller med store datamængder er det ikke en triviel opgave at vedligeholde to parallelle tabeller. Du skal også bestemme, hvordan du flytter data fra det gamle til det nye skema. Dette kan nogle gange kræve planlagt nedetid eller meget sofistikeret infrastruktur. Denne proces introducerer nødvendigvis både risiko og ekstra arbejde.

Dataintegritetsproblemer

Når flere hold bruger en delt tabel til at vælge og opdatere kritiske data, kan det nemt blive en "slagmark". Resultatet er, at forretningslogikken ender med at blive spredt på tværs af forskellige tjenester, og der er ingen centraliseret kontrol over dataintegriteten. Det bliver meget svært at vide, hvorfor et bestemt felt er gemt på en bestemt måde, hvem der kan opdatere det, og hvad der sker, hvis det efterlades tomt.

Fejlretnings- og overvågningsudfordringer

Antag for eksempel, at tabellen går i stykker: Lad os sige, at der er dårlige data, eller at nogen har låst nogle vigtige rækker. At identificere kilden til problemet kan ofte kræve, at man beder alle team med DB-adgang om at bestemme, hvilken forespørgsel der forårsagede problemet. Det er ofte ikke indlysende: Det betyder, at et teams forespørgsel kan have låst databasen, mens et andet teams forespørgsel producerer den observerbare fejl.

Single-node fejl trækker alle ned.

En delt database er et enkelt fejlpunkt. Hvis det går ned, så vil mange tjenester gå ned med det. Når databasen har problemer med ydeevnen på grund af en tjenestes tunge forespørgsler, oplever alle tjenester problemer. I en model med tydelige API'er og dataejerskab er hvert team mestre over deres tjenestes tilgængelighed og ydeevne, så en fejl i én komponent forplanter sig ikke til andre.

At levere en separat skrivebeskyttet replika løser ikke problemet.

Et almindeligt kompromis er: "Vi giver dig en skrivebeskyttet replika, så du kan forespørge uden at påvirke vores hoveddatabase." I første omgang kan det løse nogle belastningsproblemer, men:

  • Der er fortsat problemer med versionering. Hovedproblemet er, at når hovedtabelstrukturen ændres, ændres replikaens struktur også, bare med en vis forsinkelse.


  • Replikationsforsinkelse kan få datatilstande til at være uforudsigelige, især med store datasæt.


  • Ejerskab er stadig uklart: Hvem definerer formatet, strukturen og brugsreglerne? En replika er stadig "et stykke" af en andens database.

Sådan designes serviceinteraktion korrekt (kontrakt først)

En eksplicit kontraktdefinition.

Moderne designpraksis (f.eks. "API First" eller "Contract First") starter med en formel grænsefladedefinition. Der bruges OpenAPI/Swagger-, protobuf- eller GraphQL-skemaer. På denne måde ved både mennesker og maskiner, hvilke endepunkter der er tilgængelige, hvilke felter der er påkrævet, og hvilke datatyper der bruges.

Tjeneste som dataejer

I en mikrotjenester (eller endda modulær) arkitektur er antagelsen, at hver tjeneste ejer sine data fuldstændigt. Den definerer strukturen, lagringen og forretningslogikken og giver en API for al ekstern adgang til denne API. Ingen kan røre ved 'en andens' database: kun officielle slutpunkter eller begivenheder. Dette gør livet lettere, når der er tale om ændringer, og det er altid klart, hvem der har skylden.

Implementeringseksempler

  • REST/HTTP: En tjeneste udgiver endepunkter som GET /items , POST /items osv., og klienter fremsætter anmodninger med et veldefineret dataskema (DTO).


  • gRPC / binære protokoller: I gRPC/protobuf er tjenesten og beskederne formelt defineret i .proto-filer, og der foretages blot ændringer i .proto-filerne, hvor metode, anmodning og svar er defineret.


  • Hændelsesdrevet: Dataejertjenesten udgiver begivenheder til en mægler som Kafka eller RabbitMQ, og abonnenter forbruger dem. Kontrakten her er arrangementets format. Strukturelle ændringer foretages gennem versionerede emner eller meddelelser.

Versionskontrol

Uanset hvilken model, er det både muligt og essentielt at implementere versionskontrol på interfacet. For eksempel:

  • I REST har vi /api/v1/… og /api/v2/.


  • Med gRPC/protobuf er der kraftfulde mekanismer til bagud/fremad kompatibilitet – nye felter, beskeder og metoder kan tilføjes uden at ødelægge gamle klienter, mens andre markeres som forældede.


  • I begivenhedsdrevne arkitekturer kan du udgive gamle og nye begivenhedsformater parallelt, indtil alle forbrugere migrerer.

Fordelt ansvar

Et grundlæggende princip er, at det team, der ejer dataene, bestemmer, hvordan de skal opbevares og administreres, men de bør ikke give direkte skriveadgang til andre tjenester. Andre skal gå gennem API'et i modsætning til at redigere udenlandske data. Dette giver en klarere ansvarsfordeling: Hvis service A er i stykker, så er det service A's ansvar at ordne den og ikke dens naboer.

Eksempler på serviceinteraktion

Inden for et enkelt team

Ved første øjekast, hvis alt er i ét team, hvorfor komplicere tingene med en API? I virkeligheden, selvom du har et enkelt produkt opdelt i moduler, kan en delt tabel føre til de samme problemer.


  • Det er bedre at oprette en "facade" eller "mikroservice", der f.eks. ejer "ordre"-tabellen, og så kalder andre moduler (som analytics) denne facade/tjeneste.


  • Dette holder kontraktprincippet eksplicit og forenkler fejlfinding.


For eksempel er ordretjenesten ejer af ordretabellen, og faktureringstjenesten får ikke direkte adgang til denne tabel – den foretager opkald til ordretjenestens slutpunkter for at få ordredetaljer eller for at markere en ordre som betalt.

Mellem to hold

På et højere niveau, når to eller flere hold er ansvarlige for forskellige områder, forbliver principperne de samme. For eksempel:

  • Team A er ansvarlig for produktkatalogtjenesten, der indeholder oplysninger om hver vare (pris, tilgængelighed, attributter).


  • Team B tager sig af indkøbskurvservicen.


Hvis Team B direkte forespørger på "Katalog"-tabellen, der tilhører Team A, kan eventuelle interne skemaændringer ved A (f.eks. tilføjelse af felter, ændring af struktur) påvirke Team B.


Den korrekte tilgang er at bruge en API: Team A leverer slutpunkter som GET /catalog/items , GET /catalog/items/{id} osv., og Team B bruger disse metoder. Hvis A er i stand til at understøtte ældre og nyere versioner, kan de frigive /v2, hvilket giver B tid til at migrere.

Organisatoriske aspekter og fordele

Gennemsigtig kommunikation

Med en formel kontrakt er alle ændringer synlige: i Swagger/OpenAPI, .proto-filer eller begivenhedsdokumentation. Enhver opdatering kan diskuteres på forhånd, testes korrekt og planlægges med bagudkompatibilitetsstrategier efter behov.

Hurtigere udvikling

Ændringer i én tjeneste har mindre indflydelse på andre. Teamet behøver ikke at bekymre sig om at "knække" en anden, hvis de administrerer nye og gamle felter eller endepunkter korrekt, hvilket sikrer en glidende overgang.

Adgangs- og sikkerhedsstyring

API-gateways, godkendelse og godkendelse (JWT, OAuth) er standard for tjenester, men næsten umulige med en delt tabel. Det er nemmere at finjustere adgangen (hvem kan kalde hvilke metoder), føre logfiler, spore brugsstatistikker og pålægge kvoter. Dette gør systemet mere sikkert og mere forudsigeligt.

Konklusion

En delt tabel i databasen er en implementeringsdetalje snarere end en aftale mellem tjenester og betragtes derfor ikke som en kontrakt. De mange problemer (kompleks versionering, kaotiske ændringer, uklart ejerskab, sikkerhed og ydeevnerisici) gør denne tilgang uholdbar i det lange løb.


Den korrekte tilgang er Contract First , hvilket betyder at definere interaktion gennem formelt design og følge princippet om, at hver tjeneste forbliver ejeren af sine data. Dette hjælper ikke kun med at mindske teknisk gæld, men øger også gennemsigtigheden, fremskynder produktudviklingen og muliggør sikre ændringer uden at skulle engagere sig i brandslukning over databaseskemaer.


Det er både et teknisk spørgsmål (hvordan man designer og integrerer) og et organisatorisk spørgsmål (hvordan teams kommunikerer og håndterer ændringer). Hvis du vil have dit produkt til at vokse uden at skulle håndtere endeløse nødsituationer vedrørende databaseskemaer, så bør du begynde at tænke i kontrakter frem for direkte databaseadgang.