Komandalar iş qaydalarını (business rules) əsas servisdən ayırmaq istədikdə, çox vaxt bu qərarı texnoloji stack üzərindən qiymətləndirirlər. Node.js'də "hansı npm paketi bu işi görəcək?", Java'da isə "Drools'u gətirəkmi?" sualları gündəmə gəlir. Məşhur texniki bloq yazısında qeyd edildiyi kimi, bu sualların hər ikisi eyni kök səhvə əsaslanır – onlar arxitektura qərarını, "seam" (qovşaqlıq) hələ dizayn edilməmişdən əvvəl, bir məhsul/mexanizm seçiminə çevirirlər.
Dilin Java, TypeScript və ya başqa bir şey olması fərq etmir. Doğru "seam" anlayışı eynidir: burada qaydalar idarə axını (control flow) olmaqdan çıxır və sırf məlumat (data) halına gəlir. Xidmət (service) ilə qayda qatı (rule layer) arasındakı sərhəd hər iki tərəfdən tipləşdirilmiş (typed) olur. Nümunədə göstərildiyi kimi, PricingInput və PricingDecision interfeysləri compile-time təhlükəsizlik təmin edir. Lakin compiler yalnız öz kodunuzu qoruyur – şəbəkə üzərindən gələn və səssizcə dəyişən bir qaydanı qoruya bilməz. Məhz buna görə də runtime validation (məsələn, Zod'un parse() metodu) bu qovşaqlığın vacib hissəsidir. Qayda dəyişikliyi discountPercent sahəsinin adını dəyişərsə, runtime check bunu production'da deyil, testdə aşkar edir.
Java tərəfində ənənəvi yanaşma Drools'u seçməkdir. Drools, Rete alqoritmi ilə işləyən yetkin bir mühərrikdir. Lakin o, avtomatik olaraq bu "seam"i təmin etmir. Drools'un qaydaları DRL faylları və ya decision table-lar şəklində classpath daxilində qalır. Bu o deməkdir ki, qayda dəyişikliyi hələ də əsas servisin deploy cycle'ı ilə bağlıdır. Drools, yüzlərlə qaydanın mürəkkəb pattern matching tələb etdiyi hallar üçün əla seçimdir. Lakin məqsəd sadəcə "threshold dəyərini dəyişmək üçün gözləmə müddətini aradan qaldırmaq"dırsa, Drools həddindən artıq yüklü bir həll olur. Nəticədə siz qaydanı servisdən çıxarmış olursunuz, lakin onu daha da əlçatmaz bir yerə – başqa bir Java dependency-sinə gömmüş olursunuz.
Bloq yazısının müəllifi, bu problemin həlli olaraq "Nected" platformasını təqdim edir. "Nected" məhz bu "seam"in özündə yerləşən bir Decision API-dir. Eyni qayda seti həm Node.js, həm də "Spring Boot" servisləri tərəfindən eyni typed kontrakt üzərindən HTTP çağrısı ilə istifadə edilə bilər. Bu yanaşmanın üstünlüyü ondadır ki, qayda dəyişiklikləri servislərdən asılı olmayaraq versiyalanır və audit olunur. Biznes istifadəçilər (proqramçı olmayanlar) qaydaları vizual redaktor vasitəsilə dəyişə bilər, lakin typed kontrakt (schema) toxunulmaz qalır. Həmçinin, heç bir ağır rule engine'in JVM-ə və ya Node.js prosesinə yerləşdirilməsinə ehtiyac qalmır, bu da latency problemlərini minimuma endirir.
Bu arxitekturanın ən kritik komponenti kontrakt testləridir (contract tests). Bu testlər qaydanın biznes məntiqini yox, onun qaytardığı məlumatın strukturunu (shape) yoxlayır. Testlər real qayda endpoint-inə qarşı işə salınır. Əgər qayda dəyişikliyi kontraktı pozarsa (məsələn, gözlənilən sahəni göndərməzsə), pipeline uğursuz olur. Bu, səhvin müştəriyə çatmadan aşkar edilməsini təmin edir.
Qeyd edək ki, bu nümunə (seam pattern) hər layihə üçün lazım deyil. Əgər bir komanda, bir xidmət və ildə bir neçə dəfə dəyişən qaydalarınız varsa, testlərlə əhatə olunmuş inline kod (bir neçə sətirlik if bloku) ən sadə və effektiv həll yoludur. Şəbəkə çağırışının gətirdiyi 10-15 millisaniyəlik latency bu halda sadəcə xərc deməkdir. "Seam" öz dəyərini o zaman göstərir ki, eyni qaydalar bir neçə xidmət və ya dil tərəfindən istifadə edilsin, yaxud qaydaları proqramçı olmayan şəxslər idarə etməli olsun. Xülasə, əvvəlcə arxitekturanı (seam'i) dizayn edin, sonra aləti (engine'i) seçin.


