Ծառայությունների փոխազդեցության մոդուլների համատեքստում առաջանում է անխուսափելի հարցը. Ի՞նչ կանոններով է իրականացվում հաղորդակցությունը: ՏՏ արտադրանքներում «պայմանագիրը» ներկայացնում է պաշտոնական պատկերացում, թե ինչ տվյալներ են հոսում համակարգերի միջև և ինչպես են դրանք փոխանցվում: Սա ներառում է տվյալների ձևաչափը (JSON, Protobuf և այլն), կառուցվածքային տարրեր (դաշտեր, տվյալների տեսակներ), կապի արձանագրություն (REST, gRPC, հաղորդագրությունների հերթեր) և այլ բնութագրեր:
Պայմանագիրը ապահովում է բացություն (բոլորը գիտեն, թե ինչ է ստացվել և ուղարկվել), կանխատեսելիություն (մենք կարող ենք թարմացնել պայմանագիրը և պահպանել տարբերակները) և հուսալիություն (մեր համակարգը չի ձախողվի, եթե մենք լավ կառավարվող փոփոխություններ կատարենք):
Գործնականում, թեև բոլորը խոսում են միկրոծառայությունների, «պայմանագրերի» և API-ների մասին, մենք հաճախ տեսնում ենք, որ մարդիկ որդեգրում են այն մոտեցումը.
Հետևաբար, թեև տվյալների փոխանակման համար ընդհանուր աղյուսակի օգտագործումը կարող է արդյունավետ և օպտիմիզացված թվալ արագ արդյունքների համար, այն երկարաժամկետ հեռանկարում առաջացնում է տարբեր տեխնիկական և կազմակերպչական մարտահրավերներ: Այնուամենայնիվ, երբ թիմերը ընտրում են ընդհանուր աղյուսակներ տվյալների փոխանակման համար, նրանք կարող են բախվել բազմաթիվ խնդիրների իրականացման ընթացքում:
Երբ ծառայությունները հաղորդակցվում են REST/gRPC/GraphQL-ի միջոցով, դրանք ունեն պաշտոնական սահմանում՝ OpenAPI (Swagger), պրոտոբուֆ սխեմաներ կամ GraphQL սխեմաներ: Սրանք մանրամասնորեն սահմանում են, թե որ ռեսուրսները (վերջնական կետերը) են հասանելի, որ դաշտերը սպասվում են, դրանց տեսակները և հարցում/պատասխանի ձևաչափերը: Երբ «ընդհանուր աղյուսակը» գործում է որպես պայմանագիր, չկա պաշտոնական նկարագրություն. չկա պայմանագրի պաշտոնական նկարագրություն. հասանելի է միայն աղյուսակի սխեման (DDL), և նույնիսկ դա լավ փաստագրված չէ: Աղյուսակի կառուցվածքի ցանկացած աննշան փոփոխություն (օրինակ՝ սյունակի ավելացում կամ ջնջում, տվյալների տեսակների փոփոխություն) կարող է ազդել այլ թիմերի վրա, որոնք կարդում կամ գրում են այս աղյուսակից:
API-ի տարբերակավորումը սովորական պրակտիկա է. մենք կարող ենք ունենալ v1, v2 և այլն, և մենք կարող ենք պահպանել հետընթաց համատեղելիությունը, այնուհետև աստիճանաբար հաճախորդներին տեղափոխել ավելի նոր տարբերակներ: Տվյալների բազայի աղյուսակների համար մենք ունենք միայն DDL գործողություններ (օրինակ՝ ALTER TABLE
), որոնք սերտորեն կապված են որոշակի DB շարժիչի հետ և պահանջում են միգրացիաների մանրակրկիտ մշակում:
Չկա կենտրոնացված համակարգ, որը կարող է ծանուցումներ ուղարկել սպառողներին սխեմայի փոփոխությունների մասին, որոնք պահանջում են թարմացնել իրենց հարցումները: Արդյունքում կարող են տեղի ունենալ «սեղանի տակ» գործարքներ . ինչ-որ մեկը կարող է չաթում տեղադրել «Վաղը մենք X սյունակը կփոխենք Y-ի», բայց երաշխիք չկա, որ բոլորը ժամանակին պատրաստ կլինեն:
Երբ կա հստակ սահմանված API, ակնհայտ է, թե ում է պատկանում այն՝ ծառայությունը, որը ծառայում է որպես API հրատարակիչ: Երբ մի քանի թիմեր օգտագործում են տվյալների բազայի միևնույն աղյուսակը, շփոթություն է առաջանում, թե ով կարող է որոշել կառուցվածքը և որ դաշտերը պահել և ինչպես մեկնաբանել դրանք: Արդյունքում, աղյուսակը կարող է դառնալ «ոչ մեկի սեփականությունը», և յուրաքանչյուր փոփոխություն դառնում է որոնում. «Մենք պետք է ստուգենք մյուս թիմը, եթե նրանք օգտագործում են հին սյունակը»:
Դժվար է հետևել, թե ով կարող է կարդալ և գրել սեղանի վրա, եթե շատ թիմեր մուտք ունեն DB: Հնարավորություն կա, որ չարտոնված ծառայությունները կարող են մուտք գործել տվյալներ, թեև դրանք նախատեսված չեն նրանց համար: Ավելի հեշտ է կառավարել նման խնդիրները API-ով. Դուք կարող եք վերահսկել մուտքի իրավունքները (ով կարող է զանգահարել, թե որ մեթոդներին), օգտագործել նույնականացում և թույլտվություն և վերահսկել, թե ով ինչ է զանգահարել: Սեղանի դեպքում դա շատ ավելի բարդ է:
Տվյալների ցանկացած ներքին փոփոխություն (ինդեքսների վերակազմավորում, աղյուսակի բաժանում, DB-ի փոփոխություն) դառնում է գլոբալ խնդիր: Եթե աղյուսակը գործում է որպես հանրային ինտերֆեյս, սեփականատերը չի կարող ներքին փոփոխություններ կատարել՝ առանց վտանգի ենթարկելու բոլոր արտաքին ընթերցողներին և գրողներին:
Սա ամենացավալին կողմն է. ինչպե՞ս կարելի է մեկ այլ թիմին տեղեկացնել, որ հաջորդ օրը սխեման փոխվելու է:
Երբ մի քանի թիմեր օգտագործում են ընդհանուր աղյուսակը կարևոր տվյալներ ընտրելու և թարմացնելու համար, այն հեշտությամբ կարող է դառնալ «ռազմի դաշտ»: Արդյունքն այն է, որ բիզնեսի տրամաբանությունն ավարտվում է ցրված տարբեր ծառայություններում, և տվյալների ամբողջականության կենտրոնացված վերահսկողություն չկա: Շատ դժվար է դառնում իմանալ, թե ինչու է որոշակի դաշտը պահվում որոշակի ձևով, ով կարող է թարմացնել այն և ինչ է տեղի ունենում, եթե այն դատարկ մնա:
Օրինակ, ենթադրենք, որ աղյուսակը կոտրվում է. ասենք, վատ տվյալներ կան կամ ինչ-որ մեկը կողպել է որոշ կարևոր տողեր: Խնդրի աղբյուրը բացահայտելու համար հաճախ կարող է պահանջվել DB մուտք ունեցող յուրաքանչյուր թիմից խնդրել՝ որոշելու, թե որ հարցումն է առաջացրել խնդիրը: Դա հաճախ ակնհայտ չէ. սա նշանակում է, որ մի թիմի հարցումը կարող է արգելափակել տվյալների բազան, մինչդեռ մյուս թիմի հարցումն առաջացնում է նկատելի սխալ:
Համօգտագործվող տվյալների բազան ձախողման մեկ կետ է: Եթե այն իջնի, ապա դրա հետ շատ ծառայություններ կիջնեն։ Երբ տվյալների բազան աշխատանքի հետ կապված խնդիրներ ունի մեկ ծառայության ծանր հարցումների պատճառով, բոլոր ծառայությունները խնդիրներ են ունենում: Հստակ API-ներով և տվյալների սեփականություն ունեցող մոդելում յուրաքանչյուր թիմ տիրապետում է իր ծառայության հասանելիությանը և կատարողականին, ուստի մի բաղադրիչի ձախողումը չի տարածվում մյուսների վրա:
Ընդհանուր փոխզիջումը հետևյալն է. «Մենք ձեզ կտրամադրենք միայն կարդալու կրկնօրինակ, որպեսզի կարողանաք հարցումներ կատարել՝ առանց մեր հիմնական տվյալների բազայի վրա ազդելու»: Սկզբում դա կարող է լուծել ծանրաբեռնվածության որոշ խնդիրներ, բայց.
Դիզայնի ժամանակակից պրակտիկան (օրինակ՝ «API First» կամ «Contract First») սկսվում է ինտերֆեյսի պաշտոնական սահմանմամբ: Օգտագործվում են OpenAPI/Swagger, protobuf կամ GraphQL սխեմաներ: Այսպիսով, և՛ մարդիկ, և՛ մեքենաները գիտեն, թե որ վերջնակետերն են հասանելի, որ դաշտերն են պահանջվում և ինչ տվյալների տեսակներ են օգտագործվում:
Միկրոծառայությունների (կամ նույնիսկ մոդուլային) ճարտարապետության մեջ ենթադրվում է, որ յուրաքանչյուր ծառայություն ամբողջությամբ տիրապետում է իր տվյալներին: Այն սահմանում է կառուցվածքը, պահեստավորումը և բիզնես տրամաբանությունը և ապահովում է API՝ այդ API-ի բոլոր արտաքին մուտքի համար: Ոչ ոք չի կարող դիպչել «ուրիշի» տվյալների բազային. միայն պաշտոնական վերջնական կետերը կամ իրադարձությունները: Սա հեշտացնում է կյանքը, երբ փոփոխությունները հարցականի տակ են, և միշտ պարզ է, թե ով է մեղավոր:
GET /items
, POST /items
, և այլն, և հաճախորդները հարցումներ են կատարում տվյալների հստակ սահմանված սխեմայով (DTO):
Անկախ նրանից, թե որ մոդելն է, ինտերֆեյսի վրա տարբերակի վերահսկումն իրականացնելը և՛ հնարավոր է, և՛ կարևոր: Օրինակ՝
Հիմնարար սկզբունքն այն է, որ թիմը, որը տիրապետում է տվյալներին, պետք է որոշի, թե ինչպես պահել և կառավարել դրանք, բայց նրանք չպետք է ուղղակիորեն մուտքագրեն այլ ծառայություններ: Մյուսները պետք է անցնեն API-ի միջոցով՝ ի տարբերություն օտարերկրյա տվյալների խմբագրման: Սա հանգեցնում է պատասխանատվության ավելի հստակ բաշխման. Եթե A ծառայությունը խափանված է, ապա դա շտկելու ծառայության պարտականությունն է ոչ թե հարևանները:
Առաջին հայացքից, եթե ամեն ինչ մեկ թիմում է, ինչո՞ւ բարդացնել ամեն ինչ API-ով: Իրականում, նույնիսկ եթե դուք ունեք մեկ արտադրանք, որը բաժանված է մոդուլների, ընդհանուր աղյուսակը կարող է հանգեցնել նույն խնդիրների:
Օրինակ՝ Պատվերների ծառայությունը պատվերների աղյուսակի սեփականատերն է, և Billing ծառայությունն ուղղակիորեն մուտք չի գործում այդ աղյուսակը. այն զանգեր է կատարում պատվերների ծառայության վերջնակետեր՝ պատվերի մանրամասները ստանալու կամ պատվերը որպես վճարված նշելու համար:
Ավելի բարձր մակարդակում, երբ երկու կամ ավելի թիմեր պատասխանատու են տարբեր ոլորտների համար, սկզբունքները մնում են նույնը: Օրինակ՝
Եթե B թիմն ուղղակիորեն հարցումներ կատարի A թիմին պատկանող «Կատալոգ» աղյուսակին, A-ում ներքին սխեմայի ցանկացած փոփոխություն (օրինակ՝ դաշտերի ավելացում, կառուցվածքի փոփոխություն) կարող է ազդել Բ թիմի վրա:
Ճիշտ մոտեցումը API-ի օգտագործումն է. A թիմը տրամադրում է վերջնակետեր, ինչպիսիք են GET /catalog/items
, GET /catalog/items/{id}
և այլն, և B թիմը օգտագործում է այդ մեթոդները: Եթե A-ն ի վիճակի է աջակցել ավելի հին և նոր տարբերակներին, նրանք կարող են թողարկել /v2, ինչը B-ին ժամանակ է տալիս միգրացիայի համար:
Պաշտոնական պայմանագրով բոլոր փոփոխությունները տեսանելի են՝ Swagger/OpenAPI-ում, .proto ֆայլերում կամ իրադարձությունների փաստաթղթերում: Ցանկացած թարմացում կարող է նախօրոք քննարկվել, պատշաճ կերպով փորձարկվել և պլանավորվել՝ անհրաժեշտության դեպքում հետ համատեղելիության ռազմավարություններով:
Մեկ ծառայության փոփոխությունները ավելի քիչ ազդեցություն են ունենում մյուսների վրա: Թիմը չպետք է անհանգստանա մեկ ուրիշին «կոտրելու» մասին, եթե նրանք պատշաճ կերպով կառավարեն նոր և հին դաշտերը կամ վերջնակետերը՝ ապահովելով սահուն անցում:
API-ի դարպասները, նույնականացումը և թույլտվությունը (JWT, OAuth) ստանդարտ են ծառայությունների համար, բայց գրեթե անհնար է ընդհանուր աղյուսակի դեպքում: Ավելի հեշտ է կարգավորել մուտքը (ով կարող է զանգահարել, որ մեթոդներին), պահել տեղեկամատյանները, հետևել օգտագործման վիճակագրությանը և սահմանել քվոտաներ: Սա համակարգը դարձնում է ավելի անվտանգ և կանխատեսելի:
Տվյալների բազայի համօգտագործվող աղյուսակը ավելի շուտ կատարման դետալ է, քան ծառայությունների միջև համաձայնություն, ուստի պայմանագիր չի համարվում: Բազմաթիվ խնդիրները (բարդ տարբերակների ձևավորում, քաոսային փոփոխություններ, անհասկանալի սեփականության իրավունք, անվտանգություն և կատարողականի ռիսկեր) երկարաժամկետ հեռանկարում այս մոտեցումն անհիմն են դարձնում:
Ճիշտ մոտեցումը Contract First-ն է, որը նշանակում է փոխազդեցության սահմանում պաշտոնական ձևավորման միջոցով և հետևելով այն սկզբունքին, որ յուրաքանչյուր ծառայություն մնում է իր տվյալների սեփականատերը: Սա ոչ միայն օգնում է նվազեցնել տեխնիկական պարտքը, այլև մեծացնում է թափանցիկությունը, արագացնում է արտադրանքի զարգացումը և հնարավորություն է տալիս անվտանգ փոփոխություններ կատարել՝ առանց տվյալների բազայի սխեմաների հրդեհաշիջման:
Դա և՛ տեխնիկական խնդիր է (ինչպես ձևավորել և ինտեգրվել), և՛ կազմակերպչական (ինչպես են թիմերը հաղորդակցվում և կառավարում փոփոխությունները): Եթե ցանկանում եք, որ ձեր արտադրանքը աճի առանց տվյալների բազայի սխեմաների հետ կապված անվերջ արտակարգ իրավիճակների հետ գործ ունենալու, ապա դուք պետք է սկսեք մտածել պայմանագրերի, այլ ոչ թե տվյալների բազայի ուղղակի հասանելիության մասին: