Bizning kompaniyamiz ERP toifasidagi dasturiy echimlarni ishlab chiqishga ixtisoslashgan bo'lib, unda asosiy ulush katta hajmdagi biznes mantig'i va ish jarayoni a la EDMS bilan tranzaktsion tizimlarga to'g'ri keladi. Mahsulotlarimizning zamonaviy versiyalari JavaEE texnologiyalariga asoslangan, ammo biz mikroservislar bilan ham faol tajriba o'tkazmoqdamiz. Bunday echimlarning eng muammoli sohalaridan biri qo'shni domenlar bilan bog'liq bo'lgan turli quyi tizimlarning integratsiyasidir. Biz foydalanadigan arxitektura uslublari, texnologiya steklari va ramkalaridan qat'i nazar, integratsiya vazifalari doimo bizga katta bosh og'rig'ini keltirdi, ammo yaqinda bunday muammolarni hal qilishda muvaffaqiyatlar kuzatilmoqda.
Sizning e'tiboringizga taqdim etilgan maqolada men NPO Kristaning belgilangan hududdagi tajribasi va arxitektura tadqiqotlari haqida gapiraman. Biz, shuningdek, dastur ishlab chiquvchisi nuqtai nazaridan integratsiya muammosini hal qilishning oddiy misolini ko'rib chiqamiz va bu soddalik ortida nima yashiringanligini bilib olamiz.
Rad etish
Maqolada tasvirlangan me'moriy va texnik echimlar men tomonidan muayyan vazifalar kontekstida shaxsiy tajribaga asoslangan holda taklif etiladi. Ushbu echimlar universal deb da'vo qilmaydi va boshqa foydalanish shartlarida optimal bo'lmasligi mumkin.
BPM ning bunga qanday aloqasi bor?
Bu savolga javob berish uchun biz yechimlarimizning qo'llaniladigan muammolarining o'ziga xos xususiyatlarini biroz o'rganishimiz kerak. Bizning odatiy tranzaktsion tizimimizda biznes mantiqining asosiy qismi foydalanuvchi interfeyslari orqali ma'lumotlar bazasiga ma'lumotlarni kiritish, ushbu ma'lumotlarni qo'lda va avtomatik ravishda tekshirish, uni qandaydir ish jarayoni orqali o'tkazish, boshqa tizimga / tahliliy ma'lumotlar bazasiga / arxivga nashr qilish, hisobotlarni yaratishdir. Shunday qilib, mijozlar uchun tizimning asosiy vazifasi ularning ichki biznes jarayonlarini avtomatlashtirishdir.
Qulaylik uchun biz aloqada "hujjat" atamasidan ma'lum bir ish jarayoni "biriktirilishi" mumkin bo'lgan umumiy kalit bilan birlashtirilgan ma'lumotlar to'plamining ba'zi bir mavhumligi sifatida foydalanamiz.
Ammo integratsiya mantig'i haqida nima deyish mumkin? Axir, integratsiya vazifasi mijozning iltimosiga binoan EMAS, balki butunlay boshqa omillar ta'siri ostida qismlarga "arralangan" tizim arxitekturasi tomonidan yaratiladi:
Konvey qonunining ta'siri ostida;
boshqa mahsulotlar uchun ilgari ishlab chiqilgan quyi tizimlardan qayta foydalanish natijasida;
me'mor tomonidan qaror qabul qilinganidek, funktsional bo'lmagan talablar asosida.
Biznes mantiqini integratsiya artefaktlari bilan ifloslantirmaslik va dastur ishlab chiquvchisini tizimning me'moriy landshaftining o'ziga xos xususiyatlarini o'rganishdan qutqarish uchun integratsiya mantig'ini asosiy ish jarayonining biznes mantig'idan ajratish katta vasvasa mavjud. Ushbu yondashuv bir qator afzalliklarga ega, ammo amaliyot uning samarasizligini ko'rsatadi:
integratsiya muammolarini hal qilish, odatda, asosiy ish oqimini amalga oshirishda cheklangan kengaytma nuqtalari tufayli sinxron qo'ng'iroqlar ko'rinishidagi eng oddiy variantlarga siljiydi (quyida sinxron integratsiyaning kamchiliklari haqida batafsilroq);
integratsiya artefaktlari boshqa quyi tizimdan fikr-mulohazalar zarur bo'lganda ham asosiy biznes mantig'iga kirib boradi;
dastur ishlab chiqaruvchisi integratsiyani e'tiborsiz qoldiradi va ish jarayonini o'zgartirib, uni osongina buzishi mumkin;
tizim foydalanuvchi nuqtai nazaridan bir butun bo'lishni to'xtatadi, quyi tizimlar orasidagi "choklar" sezilarli bo'ladi, ma'lumotlarni bir quyi tizimdan ikkinchisiga o'tkazishni boshlaydigan ortiqcha foydalanuvchi operatsiyalari paydo bo'ladi.
Yana bir yondashuv - integratsiyalashgan o'zaro ta'sirlarni asosiy biznes mantig'i va ish jarayonining ajralmas qismi sifatida ko'rib chiqish. Ilovalarni ishlab chiquvchilarning mahorat talablari keskin oshib ketmasligi uchun yangi integratsiya shovqinlarini yaratish oson va tabiiy ravishda, yechim tanlashning minimal variantlari bilan amalga oshirilishi kerak. Bu ko'rinadiganidan ko'ra qiyinroq: asbob foydalanuvchiga undan foydalanish uchun zarur bo'lgan turli xil variantlarni taqdim etishi va shu bilan birga oyog'iga o'q otilishiga yo'l qo'ymaslik uchun etarlicha kuchli bo'lishi kerak. Muhandis integratsiya vazifalari kontekstida javob berishi kerak bo'lgan ko'plab savollar mavjud, ammo dastur ishlab chiquvchisi kundalik ishlarida o'ylamasligi kerak: tranzaktsiyalar chegaralari, izchillik, atomiklik, xavfsizlik, masshtablash, yuk va resurslarni taqsimlash, marshrutlash, marshallash, targ'ibot va kontekstlarni o'zgartirish va hokazo. Ilovalarni ishlab chiquvchilarga juda oddiy qarorlar shablonlarini taklif qilish kerak, ularda bunday savollarga javoblar allaqachon yashiringan. Ushbu naqshlar etarlicha xavfsiz bo'lishi kerak: biznes mantig'i juda tez-tez o'zgarib turadi, bu xatolarni kiritish xavfini oshiradi, xatolar narxi ancha past darajada qolishi kerak.
Ammo shunga qaramay, BPM ning bunga nima aloqasi bor? Ish jarayonini amalga oshirishning ko'plab variantlari mavjud ...
Haqiqatan ham, biznes-jarayonlarning yana bir amalga oshirilishi bizning echimlarimizda juda mashhur - davlat o'tish diagrammasining deklarativ sozlamalari va o'tishlarga biznes mantig'i bilan ishlov beruvchilarni ulash orqali. Shu bilan birga, ish jarayonida "hujjat" ning hozirgi holatini belgilovchi davlat "hujjat" ning o'ziga xos atributidir.
Loyihaning boshida jarayon shunday ko'rinadi
Bunday amalga oshirishning mashhurligi chiziqli biznes jarayonlarini yaratishning nisbatan soddaligi va tezligi bilan bog'liq. Biroq, dasturiy ta'minot tizimlarining murakkablashishi bilan biznes jarayonining avtomatlashtirilgan qismi o'sib boradi va murakkablashadi. Har bir filial parallel ravishda bajarilishi uchun parchalanish, jarayonlarning qismlarini qayta ishlatish, shuningdek, vilkalar jarayonlariga ehtiyoj bor. Bunday sharoitda asbob noqulay bo'lib qoladi va holatga o'tish diagrammasi axborot mazmunini yo'qotadi (integratsiya o'zaro ta'siri diagrammada umuman aks ettirilmaydi).
Talablarni aniqlashtirishning bir necha marta takrorlanishidan keyin jarayon shunday ko'rinadi
Ushbu vaziyatdan chiqish yo'li dvigatelning integratsiyasi edi jBPM eng murakkab biznes jarayonlariga ega bo'lgan ba'zi mahsulotlarga. Qisqa muddatda ushbu yechim biroz muvaffaqiyatga erishdi: yozuvda juda ma'lumotli va dolzarb diagrammani saqlab, murakkab biznes jarayonlarini amalga oshirish mumkin bo'ldi. BPMN2.
Murakkab biznes jarayonining kichik qismi
Uzoq muddatda yechim umidlarni oqlamadi: vizual vositalar yordamida biznes-jarayonlarni yaratishda yuqori mehnat zichligi maqbul samaradorlik ko'rsatkichlariga erishishga imkon bermadi va asbobning o'zi ishlab chiquvchilar orasida eng yoqmaydigan narsalardan biriga aylandi. Dvigatelning ichki tuzilishi haqida ham shikoyatlar bor edi, bu ko'plab "yamoqlar" va "tayoqchalar" paydo bo'lishiga olib keldi.
JBPM-dan foydalanishning asosiy ijobiy tomoni biznes-jarayon namunasi uchun o'zining doimiy holatiga ega bo'lishning foydalari va zararlarini anglash edi. Shuningdek, biz signallar va xabarlar orqali asinxron shovqinlardan foydalangan holda turli ilovalar o'rtasida murakkab integratsiya protokollarini amalga oshirish uchun jarayon yondashuvidan foydalanish imkoniyatini ko'rdik. Bunda doimiy davlatning mavjudligi hal qiluvchi rol o'ynaydi.
Yuqoridagilarga asoslanib, biz xulosa qilishimiz mumkin: BPM uslubidagi jarayon yondashuvi bizga tobora murakkab bo'lgan biznes jarayonlarini avtomatlashtirish bo'yicha keng ko'lamli vazifalarni hal qilishga, integratsiya faoliyatini ushbu jarayonlarga uyg'unlashtirishga va amalga oshirilgan jarayonni mos yozuvda vizual ravishda aks ettirish qobiliyatini saqlab qolishga imkon beradi.
Integratsiya sxemasi sifatida sinxron qo'ng'iroqlarning kamchiliklari
Sinxron integratsiya eng oddiy blokirovka qiluvchi qo'ng'iroqni anglatadi. Bitta quyi tizim server tomoni sifatida ishlaydi va APIni kerakli usul bilan ochadi. Boshqa quyi tizim mijoz tomoni sifatida ishlaydi va kerakli vaqtda natijani kutgan holda qo'ng'iroq qiladi. Tizimning arxitekturasiga qarab, mijoz va server tomonlari bir xil dastur va jarayonda yoki turli xillarida joylashtirilishi mumkin. Ikkinchi holda, siz RPC-ning ba'zi bir amalga oshirilishini qo'llashingiz va qo'ng'iroqning parametrlari va natijasini marshalllashni ta'minlashingiz kerak.
Bunday integratsiya sxemasi juda katta kamchiliklarga ega, ammo u soddaligi tufayli amalda juda keng qo'llaniladi. Amalga oshirish tezligi sizni o'ziga jalb qiladi va uni "yonib ketish" muddati sharoitida qayta-qayta qo'llashga majbur qiladi, yechimni texnik qarzga yozadi. Ammo tajribasiz ishlab chiquvchilar uni ongsiz ravishda ishlatishadi, shunchaki salbiy oqibatlarni sezmaydilar.
Quyi tizimlar ulanishining eng aniq o'sishiga qo'shimcha ravishda, tranzaktsiyalarni "tarqatish" va "cho'zish" bilan bog'liq kamroq aniq muammolar mavjud. Haqiqatan ham, agar biznes mantig'i har qanday o'zgarishlarni amalga oshirsa, tranzaktsiyalar ajralmas hisoblanadi va tranzaktsiyalar, o'z navbatida, ushbu o'zgarishlardan ta'sirlangan ma'lum dastur resurslarini bloklaydi. Ya'ni, bitta quyi tizim boshqasidan javob kutmaguncha, u tranzaktsiyani yakunlay olmaydi va qulflarni bo'shata olmaydi. Bu turli xil ta'sirlar xavfini sezilarli darajada oshiradi:
tizimning sezgirligi yo'qoladi, foydalanuvchilar so'rovlarga javob berish uchun uzoq vaqt kutishadi;
server, odatda, to'lib-toshgan iplar hovuzi tufayli foydalanuvchi so'rovlariga javob berishni to'xtatadi: ko'pgina iplar tranzaksiya bilan band bo'lgan resurs qulfida "turib turadi";
tiqilib qolishlar paydo bo'la boshlaydi: ularning paydo bo'lish ehtimoli tranzaktsiyalar davomiyligiga, biznes mantig'i va tranzaktsiyaga jalb qilingan qulflar miqdoriga juda bog'liq;
tranzaksiya muddati tugashi bilan bog'liq xatolar paydo bo'ladi;
agar vazifa katta hajmdagi ma'lumotlarni qayta ishlash va o'zgartirishni talab qilsa, server OutOfMemory-ga "tushadi" va sinxron integratsiyalarning mavjudligi ishlov berishni "engilroq" tranzaktsiyalarga ajratishni juda qiyinlashtiradi.
Arxitektura nuqtai nazaridan, integratsiya jarayonida blokirovka qiluvchi qo'ng'iroqlardan foydalanish alohida quyi tizimlar sifatini nazorat qilishni yo'qotishiga olib keladi: bitta quyi tizimning sifat ko'rsatkichlarini boshqa quyi tizimning sifat maqsadlaridan ajratilgan holda ta'minlash mumkin emas. Agar quyi tizimlar turli jamoalar tomonidan ishlab chiqilgan bo'lsa, bu katta muammo.
Agar integratsiya qilinayotgan quyi tizimlar turli ilovalarda bo'lsa va ikkala tomondan sinxron o'zgarishlar qilish kerak bo'lsa, ishlar yanada qiziqarli bo'ladi. Ushbu o'zgarishlarni qanday qilib tranzaksiya qilish mumkin?
Agar alohida tranzaktsiyalarda o'zgarishlar amalga oshirilsa, unda mustahkam istisnolarni qayta ishlash va kompensatsiyani ta'minlash kerak bo'ladi va bu sinxron integratsiyaning asosiy afzalligi - soddalikni butunlay yo'q qiladi.
Taqsimlangan tranzaktsiyalar ham esga tushadi, ammo biz ularni o'z yechimlarimizda ishlatmaymiz: ishonchlilikni ta'minlash qiyin.
"Saga" bitimlar muammosiga yechim sifatida
Mikroservislarning ommaviyligi ortib borayotgani sababli, ularga talab ortib bormoqda Saga naqsh.
Ushbu naqsh uzoq muddatli tranzaktsiyalarning yuqoridagi muammolarini mukammal hal qiladi, shuningdek, tizimning holatini biznes mantig'i nuqtai nazaridan boshqarish imkoniyatlarini kengaytiradi: muvaffaqiyatsiz bitimdan keyin kompensatsiya tizimni asl holatiga qaytarmasligi mumkin, balki muqobil variantni taqdim etadi. ma'lumotlarni qayta ishlash yo'nalishi. Bundan tashqari, jarayonni "yaxshi" yakunlashga harakat qilganingizda, muvaffaqiyatli bajarilgan ma'lumotlarni qayta ishlash bosqichlarini takrorlamaslikka imkon beradi.
Qizig'i shundaki, monolit tizimlarda bu naqsh bo'shashmasdan bog'langan quyi tizimlarning integratsiyasi haqida gap ketganda ham dolzarb bo'lib, uzoq muddatli tranzaktsiyalar va tegishli resurs blokirovkalari natijasida yuzaga keladigan salbiy ta'sirlar mavjud.
BPM uslubidagi biznes jarayonlarimizga kelsak, Sagasni amalga oshirish juda oson: Sagasning individual bosqichlari biznes-jarayon doirasidagi faoliyat sifatida belgilanishi mumkin va biznes-jarayonning barqaror holati, shu jumladan, boshqa narsalarni belgilaydi. , dostonlarning ichki holati. Ya'ni, bizga qo'shimcha muvofiqlashtirish mexanizmi kerak emas. Sizga kerak bo'lgan yagona narsa - transport sifatida "kamida bir marta" kafolatlarni qo'llab-quvvatlaydigan xabar brokeri.
Ammo bunday yechimning ham o'ziga xos "narxi" bor:
biznes mantig'i yanada murakkablashadi: siz kompensatsiyani ishlab chiqishingiz kerak;
monolitik tizimlar uchun ayniqsa sezgir bo'lishi mumkin bo'lgan to'liq mustahkamlikdan voz kechish kerak bo'ladi;
arxitektura biroz murakkablashadi, xabar brokeriga qo'shimcha ehtiyoj paydo bo'ladi;
qo'shimcha monitoring va boshqaruv vositalari talab qilinadi (garchi umuman olganda bu hatto yaxshi: tizim xizmatining sifati oshadi).
Monolit tizimlar uchun "Sags" dan foydalanishning asosi unchalik aniq emas. Mikroservislar va boshqa SOAlar uchun, ehtimol, broker allaqachon mavjud bo'lgan va loyiha boshida to'liq izchillik qurbon qilingan bo'lsa, ushbu naqshdan foydalanishning afzalliklari kamchiliklardan sezilarli darajada ustun bo'lishi mumkin, ayniqsa, agar qulay API mavjud bo'lsa. biznes mantiq darajasi.
Mikroservislarda biznes mantiqining inkapsulyatsiyasi
Mikroservislar bilan tajriba o'tkazishni boshlaganimizda, oqilona savol tug'ildi: domen ma'lumotlarining barqarorligini ta'minlaydigan xizmatga nisbatan domen biznes mantig'ini qaerga qo'yish kerak?
Turli xil BPMS arxitekturasini ko'rib chiqayotganda, biznes mantig'ini qat'iylikdan ajratish maqsadga muvofiq bo'lib tuyulishi mumkin: domen biznes mantig'ini bajarish uchun muhit va konteynerni tashkil etuvchi platforma va domenga bog'liq bo'lmagan mikroservislar qatlamini yarating va domen ma'lumotlarining barqarorligini alohida tartibga soling. juda oddiy va engil mikroservislar qatlami. Bu holda biznes jarayonlari qat'iylik qatlami xizmatlarini tartibga soladi.
Ushbu yondashuv juda katta plyusga ega: siz platformaning funksionalligini xohlaganingizcha oshirishingiz mumkin va faqat platforma mikroservislarining tegishli qatlami bundan "yog'lanadi". Har qanday domendagi biznes jarayonlar yangilangandan so'ng darhol platformaning yangi funksiyalaridan foydalanish imkoniyatiga ega bo'ladi.
Batafsilroq o'rganish ushbu yondashuvning muhim kamchiliklarini aniqladi:
bir vaqtning o'zida ko'plab domenlarning biznes mantig'ini bajaradigan platforma xizmati bitta muvaffaqiyatsizlik nuqtasi sifatida katta xavflarni o'z ichiga oladi. Biznes mantiqining tez-tez o'zgarishi tizim miqyosida nosozliklarga olib keladigan xatolar xavfini oshiradi;
ishlash muammolari: biznes mantig'i o'z ma'lumotlari bilan tor va sekin interfeys orqali ishlaydi:
ma'lumotlar yana bir marta yig'iladi va tarmoq stek orqali pompalanadi;
domen xizmati ko'pincha xizmatning tashqi API darajasida so'rovlarni parametrlash imkoniyatlari etarli emasligi sababli qayta ishlash uchun biznes mantig'idan ko'ra ko'proq ma'lumotlarni qaytaradi;
biznes mantig'ining bir nechta mustaqil qismlari bir xil ma'lumotlarni qayta ishlash uchun qayta-qayta so'rashi mumkin (siz ma'lumotlarni keshlash uchun seans loviyalarini qo'shish orqali bu muammoni yumshata olasiz, ammo bu arxitekturani yanada murakkablashtiradi va ma'lumotlarning yangiligi va keshni bekor qilish muammolarini keltirib chiqaradi);
tranzaksiya masalalari:
platforma xizmati tomonidan saqlanadigan doimiy holatga ega biznes jarayonlari domen ma'lumotlariga mos kelmaydi va bu muammoni hal qilishning oson yo'llari yo'q;
domen ma'lumotlarining qulfini tranzaktsiyadan tashqariga ko'chirish: agar domen biznes mantig'iga o'zgartirishlar kiritish kerak bo'lsa, birinchi navbatda haqiqiy ma'lumotlarning to'g'riligini tekshirgandan so'ng, qayta ishlangan ma'lumotlarning raqobatbardosh o'zgarishi ehtimolini istisno qilish kerak. Ma'lumotlarning tashqi blokirovkasi muammoni hal qilishga yordam beradi, ammo bunday yechim qo'shimcha xavflarni keltirib chiqaradi va tizimning umumiy ishonchliligini pasaytiradi;
yangilashda qo'shimcha asoratlar: ba'zi hollarda siz qat'iylik xizmati va biznes mantiqini sinxron yoki qat'iy ketma-ketlikda yangilashingiz kerak.
Oxir-oqibat, men asoslarga qaytishim kerak edi: domen ma'lumotlari va domen biznes mantig'ini bitta mikroservisga qamrab olish. Ushbu yondashuv mikroservisni tizimning ajralmas komponenti sifatida qabul qilishni soddalashtiradi va yuqoridagi muammolarni keltirib chiqarmaydi. Bu ham bepul emas:
API standartlashtirish biznes mantig'i (xususan, biznes jarayonlarining bir qismi sifatida foydalanuvchi faoliyatini ta'minlash) va API platformasi xizmatlari bilan o'zaro ta'sir qilish uchun talab qilinadi; API o'zgarishlariga ko'proq e'tibor berish, oldinga va orqaga muvofiqlik talab qilinadi;
har bir bunday mikroservisning bir qismi sifatida biznes mantig'ining ishlashini ta'minlash uchun qo'shimcha ish vaqti kutubxonalarini qo'shish talab qilinadi va bu bunday kutubxonalar uchun yangi talablarni keltirib chiqaradi: engillik va minimal o'tish davriga bog'liqlik;
biznes mantiqini ishlab chiquvchilar kutubxona versiyalarini kuzatib borishlari kerak: agar mikroservis uzoq vaqt davomida yakunlanmagan bo'lsa, unda kutubxonalarning eskirgan versiyasi bo'lishi mumkin. Bu yangi xususiyatni qo'shish uchun kutilmagan to'siq bo'lishi mumkin va versiyalar o'rtasida mos kelmaydigan o'zgarishlar bo'lsa, bunday xizmatning eski biznes mantig'ini kutubxonalarning yangi versiyalariga ko'chirishni talab qilishi mumkin.
Bunday arxitekturada platforma xizmatlarining qatlami ham mavjud, ammo bu qatlam endi domen biznes mantig'ini bajarish uchun konteyner emas, balki yordamchi "platforma" funktsiyalarini ta'minlovchi faqat uning muhitini tashkil qiladi. Bunday qatlam nafaqat domen mikroservislarining yengilligini saqlab qolish, balki boshqaruvni markazlashtirish uchun ham kerak.
Masalan, biznes-jarayonlardagi foydalanuvchi faoliyati vazifalarni yaratadi. Biroq, vazifalar bilan ishlashda foydalanuvchi umumiy ro'yxatdagi barcha domenlardagi vazifalarni ko'rishi kerak, ya'ni domen biznes mantig'idan tozalangan tegishli vazifani ro'yxatga olish platformasi xizmati bo'lishi kerak. Ushbu kontekstda biznes mantig'ining inkapsulyatsiyasini saqlash juda muammoli va bu ushbu arxitekturaning yana bir murosasi.
Ilovalarni ishlab chiquvchining ko'zi bilan biznes jarayonlarini integratsiyalash
Yuqorida aytib o'tilganidek, dastur ishlab chiquvchisi yaxshi rivojlanish unumdorligiga ishonish uchun bir nechta ilovalarning o'zaro ta'sirini amalga oshirishning texnik va muhandislik xususiyatlaridan mavhum bo'lishi kerak.
Keling, maqola uchun maxsus ixtiro qilingan juda qiyin integratsiya muammosini hal qilishga harakat qilaylik. Bu uchta dasturni o'z ichiga olgan "o'yin" vazifasi bo'ladi, ularning har biri ba'zi domen nomini belgilaydi: "app1", "app2", "app3".
Har bir ilova ichida integratsiya avtobusi orqali "to'p o'ynashni" boshlaydigan biznes jarayonlari ishga tushiriladi. "Ball" nomli xabarlar to'p rolini o'ynaydi.
O'yin qoidalari:
birinchi o'yinchi tashabbuskor hisoblanadi. U boshqa o'yinchilarni o'yinga taklif qiladi, o'yinni boshlaydi va istalgan vaqtda tugatishi mumkin;
boshqa o'yinchilar o'yinda ishtirok etishlarini e'lon qiladilar, bir-birlari va birinchi o'yinchi bilan "tanishadilar";
to'pni qabul qilgandan so'ng, o'yinchi boshqa ishtirokchi o'yinchini tanlaydi va to'pni unga uzatadi. O'tishlarning umumiy soni hisobga olinadi;
har bir o'yinchida "energiya" bor, bu o'sha o'yinchi tomonidan to'pning har bir o'tishi bilan kamayadi. Energiya tugagach, o'yinchi o'yindan chetlatiladi va nafaqaga chiqqanini e'lon qiladi;
agar o'yinchi yolg'iz qolsa, u darhol ketishini e'lon qiladi;
barcha o'yinchilar chiqarib yuborilganda, birinchi o'yinchi o'yin tugaganligini e'lon qiladi. Agar u o'yinni oldinroq tark etgan bo'lsa, uni yakunlash uchun o'yinni kuzatish qoladi.
Ushbu muammoni hal qilish uchun men DSL-ni biznes jarayonlari uchun ishlataman, bu sizga Kotlin-dagi mantiqni ixcham, minimal boilerplate bilan tasvirlash imkonini beradi.
App1 ilovasida birinchi o'yinchining biznes jarayoni (u ham o'yin tashabbuskori) ishlaydi:
InitialPlayer sinfi
import ru.krista.bpm.ProcessInstance
import ru.krista.bpm.runtime.ProcessImpl
import ru.krista.bpm.runtime.constraint.UniqueConstraints
import ru.krista.bpm.runtime.dsl.processModel
import ru.krista.bpm.runtime.dsl.taskOperation
import ru.krista.bpm.runtime.instance.MessageSendInstance
data class PlayerInfo(val name: String, val domain: String, val id: String)
class PlayersList : ArrayList<PlayerInfo>()
// ΠΡΠΎ ΠΊΠ»Π°ΡΡ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡΠ° ΠΏΡΠΎΡΠ΅ΡΡΠ°: ΠΈΠ½ΠΊΠ°ΠΏΡΡΠ»ΠΈΡΡΠ΅Ρ Π΅Π³ΠΎ Π²Π½ΡΡΡΠ΅Π½Π½Π΅Π΅ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅
class InitialPlayer : ProcessImpl<InitialPlayer>(initialPlayerModel) {
var playerName: String by persistent("Player1")
var energy: Int by persistent(30)
var players: PlayersList by persistent(PlayersList())
var shotCounter: Int = 0
}
// ΠΡΠΎ Π΄Π΅ΠΊΠ»Π°ΡΠ°ΡΠΈΡ ΠΌΠΎΠ΄Π΅Π»ΠΈ ΠΏΡΠΎΡΠ΅ΡΡΠ°: ΡΠΎΠ·Π΄Π°Π΅ΡΡΡ ΠΎΠ΄ΠΈΠ½ ΡΠ°Π·, ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ Π²ΡΠ΅ΠΌΠΈ
// ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡΠ°ΠΌΠΈ ΠΏΡΠΎΡΠ΅ΡΡΠ° ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²ΡΡΡΠ΅Π³ΠΎ ΠΊΠ»Π°ΡΡΠ°
val initialPlayerModel = processModel<InitialPlayer>(name = "InitialPlayer",
version = 1) {
// ΠΠΎ ΠΏΡΠ°Π²ΠΈΠ»Π°ΠΌ, ΠΏΠ΅ΡΠ²ΡΠΉ ΠΈΠ³ΡΠΎΠΊ ΡΠ²Π»ΡΠ΅ΡΡΡ ΠΈΠ½ΠΈΡΠΈΠ°ΡΠΎΡΠΎΠΌ ΠΈΠ³ΡΡ ΠΈ Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±ΡΡΡ Π΅Π΄ΠΈΠ½ΡΡΠ²Π΅Π½Π½ΡΠΌ
uniqueConstraint = UniqueConstraints.singleton
// ΠΠ±ΡΡΠ²Π»ΡΠ΅ΠΌ Π°ΠΊΡΠΈΠ²Π½ΠΎΡΡΠΈ, ΠΈΠ· ΠΊΠΎΡΠΎΡΡΡ ΡΠΎΡΡΠΎΠΈΡ Π±ΠΈΠ·Π½Π΅Ρ-ΠΏΡΠΎΡΠ΅ΡΡ
val sendNewGameSignal = signal<String>("NewGame")
val sendStopGameSignal = signal<String>("StopGame")
val startTask = humanTask("Start") {
taskOperation {
processCondition { players.size > 0 }
confirmation { "ΠΠΎΠ΄ΠΊΠ»ΡΡΠΈΠ»ΠΎΡΡ ${players.size} ΠΈΠ³ΡΠΎΠΊΠΎΠ². ΠΠ°ΡΠΈΠ½Π°Π΅ΠΌ?" }
}
}
val stopTask = humanTask("Stop") {
taskOperation {}
}
val waitPlayerJoin = signalWait<String>("PlayerJoin") { signal ->
players.add(PlayerInfo(
signal.data!!,
signal.sender.domain,
signal.sender.processInstanceId))
println("... join player ${signal.data} ...")
}
val waitPlayerOut = signalWait<String>("PlayerOut") { signal ->
players.remove(PlayerInfo(
signal.data!!,
signal.sender.domain,
signal.sender.processInstanceId))
println("... player ${signal.data} is out ...")
}
val sendPlayerOut = signal<String>("PlayerOut") {
signalData = { playerName }
}
val sendHandshake = messageSend<String>("Handshake") {
messageData = { playerName }
activation = {
receiverDomain = process.players.last().domain
receiverProcessInstanceId = process.players.last().id
}
}
val throwStartBall = messageSend<Int>("Ball") {
messageData = { 1 }
activation = { selectNextPlayer() }
}
val throwBall = messageSend<Int>("Ball") {
messageData = { shotCounter + 1 }
activation = { selectNextPlayer() }
onEntry { energy -= 1 }
}
val waitBall = messageWaitData<Int>("Ball") {
shotCounter = it
}
// Π’Π΅ΠΏΠ΅ΡΡ ΠΊΠΎΠ½ΡΡΡΡΠΈΡΡΠ΅ΠΌ Π³ΡΠ°Ρ ΠΏΡΠΎΡΠ΅ΡΡΠ° ΠΈΠ· ΠΎΠ±ΡΡΠ²Π»Π΅Π½Π½ΡΡ Π°ΠΊΡΠΈΠ²Π½ΠΎΡΡΠ΅ΠΉ
startFrom(sendNewGameSignal)
.fork("mainFork") {
next(startTask)
next(waitPlayerJoin).next(sendHandshake).next(waitPlayerJoin)
next(waitPlayerOut)
.branch("checkPlayers") {
ifTrue { players.isEmpty() }
.next(sendStopGameSignal)
.terminate()
ifElse().next(waitPlayerOut)
}
}
startTask.fork("afterStart") {
next(throwStartBall)
.branch("mainLoop") {
ifTrue { energy < 5 }.next(sendPlayerOut).next(waitBall)
ifElse().next(waitBall).next(throwBall).loop()
}
next(stopTask).next(sendStopGameSignal)
}
// ΠΠ°Π²Π΅ΡΠ°Π΅ΠΌ Π½Π° Π°ΠΊΡΠΈΠ²Π½ΠΎΡΡΠΈ Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡΠ΅Π»ΡΠ½ΡΠ΅ ΠΎΠ±ΡΠ°Π±ΠΎΡΡΠΈΠΊΠΈ Π΄Π»Ρ Π»ΠΎΠ³ΠΈΡΠΎΠ²Π°Π½ΠΈΡ
sendNewGameSignal.onExit { println("Let's play!") }
sendStopGameSignal.onExit { println("Stop!") }
sendPlayerOut.onExit { println("$playerName: I'm out!") }
}
private fun MessageSendInstance<InitialPlayer, Int>.selectNextPlayer() {
val player = process.players.random()
receiverDomain = player.domain
receiverProcessInstanceId = player.id
println("Step ${process.shotCounter + 1}: " +
"${process.playerName} >>> ${player.name}")
}
Biznes mantiqini bajarishdan tashqari, yuqoridagi kod diagramma sifatida ko'rsatilishi mumkin bo'lgan biznes jarayonining ob'ekt modelini ishlab chiqishi mumkin. Biz hali vizualizatorni amalga oshirmadik, shuning uchun rasm chizishga biroz vaqt sarflashimiz kerak edi (bu erda diagrammaning yuqoridagi kodga mos kelishini yaxshilash uchun shlyuzlardan foydalanish bo'yicha BPMN yozuvini biroz soddalashtirdim):
app2 boshqa o'yinchining biznes jarayonini o'z ichiga oladi:
RandomPlayer sinfi
import ru.krista.bpm.ProcessInstance
import ru.krista.bpm.runtime.ProcessImpl
import ru.krista.bpm.runtime.dsl.processModel
import ru.krista.bpm.runtime.instance.MessageSendInstance
data class PlayerInfo(val name: String, val domain: String, val id: String)
class PlayersList: ArrayList<PlayerInfo>()
class RandomPlayer : ProcessImpl<RandomPlayer>(randomPlayerModel) {
var playerName: String by input(persistent = true,
defaultValue = "RandomPlayer")
var energy: Int by input(persistent = true, defaultValue = 30)
var players: PlayersList by persistent(PlayersList())
var allPlayersOut: Boolean by persistent(false)
var shotCounter: Int = 0
val selfPlayer: PlayerInfo
get() = PlayerInfo(playerName, env.eventDispatcher.domainName, id)
}
val randomPlayerModel = processModel<RandomPlayer>(name = "RandomPlayer",
version = 1) {
val waitNewGameSignal = signalWait<String>("NewGame")
val waitStopGameSignal = signalWait<String>("StopGame")
val sendPlayerJoin = signal<String>("PlayerJoin") {
signalData = { playerName }
}
val sendPlayerOut = signal<String>("PlayerOut") {
signalData = { playerName }
}
val waitPlayerJoin = signalWaitCustom<String>("PlayerJoin") {
eventCondition = { signal ->
signal.sender.processInstanceId != process.id
&& !process.players.any { signal.sender.processInstanceId == it.id}
}
handler = { signal ->
players.add(PlayerInfo(
signal.data!!,
signal.sender.domain,
signal.sender.processInstanceId))
}
}
val waitPlayerOut = signalWait<String>("PlayerOut") { signal ->
players.remove(PlayerInfo(
signal.data!!,
signal.sender.domain,
signal.sender.processInstanceId))
allPlayersOut = players.isEmpty()
}
val sendHandshake = messageSend<String>("Handshake") {
messageData = { playerName }
activation = {
receiverDomain = process.players.last().domain
receiverProcessInstanceId = process.players.last().id
}
}
val receiveHandshake = messageWait<String>("Handshake") { message ->
if (!players.any { message.sender.processInstanceId == it.id}) {
players.add(PlayerInfo(
message.data!!,
message.sender.domain,
message.sender.processInstanceId))
}
}
val throwBall = messageSend<Int>("Ball") {
messageData = { shotCounter + 1 }
activation = { selectNextPlayer() }
onEntry { energy -= 1 }
}
val waitBall = messageWaitData<Int>("Ball") {
shotCounter = it
}
startFrom(waitNewGameSignal)
.fork("mainFork") {
next(sendPlayerJoin)
.branch("mainLoop") {
ifTrue { energy < 5 || allPlayersOut }
.next(sendPlayerOut)
.next(waitBall)
ifElse()
.next(waitBall)
.next(throwBall)
.loop()
}
next(waitPlayerJoin).next(sendHandshake).next(waitPlayerJoin)
next(waitPlayerOut).next(waitPlayerOut)
next(receiveHandshake).next(receiveHandshake)
next(waitStopGameSignal).terminate()
}
sendPlayerJoin.onExit { println("$playerName: I'm here!") }
sendPlayerOut.onExit { println("$playerName: I'm out!") }
}
private fun MessageSendInstance<RandomPlayer, Int>.selectNextPlayer() {
val player = if (process.players.isNotEmpty())
process.players.random()
else
process.selfPlayer
receiverDomain = player.domain
receiverProcessInstanceId = player.id
println("Step ${process.shotCounter + 1}: " +
"${process.playerName} >>> ${player.name}")
}
Diagramma:
App3 ilovasida biz o'yinchini biroz boshqacha xatti-harakatga aylantiramiz: keyingi o'yinchini tasodifiy tanlash o'rniga, u aylana algoritmiga muvofiq harakat qiladi:
sinf RoundRobinPlayer
import ru.krista.bpm.ProcessInstance
import ru.krista.bpm.runtime.ProcessImpl
import ru.krista.bpm.runtime.dsl.processModel
import ru.krista.bpm.runtime.instance.MessageSendInstance
data class PlayerInfo(val name: String, val domain: String, val id: String)
class PlayersList: ArrayList<PlayerInfo>()
class RoundRobinPlayer : ProcessImpl<RoundRobinPlayer>(roundRobinPlayerModel) {
var playerName: String by input(persistent = true,
defaultValue = "RoundRobinPlayer")
var energy: Int by input(persistent = true, defaultValue = 30)
var players: PlayersList by persistent(PlayersList())
var nextPlayerIndex: Int by persistent(-1)
var allPlayersOut: Boolean by persistent(false)
var shotCounter: Int = 0
val selfPlayer: PlayerInfo
get() = PlayerInfo(playerName, env.eventDispatcher.domainName, id)
}
val roundRobinPlayerModel = processModel<RoundRobinPlayer>(
name = "RoundRobinPlayer",
version = 1) {
val waitNewGameSignal = signalWait<String>("NewGame")
val waitStopGameSignal = signalWait<String>("StopGame")
val sendPlayerJoin = signal<String>("PlayerJoin") {
signalData = { playerName }
}
val sendPlayerOut = signal<String>("PlayerOut") {
signalData = { playerName }
}
val waitPlayerJoin = signalWaitCustom<String>("PlayerJoin") {
eventCondition = { signal ->
signal.sender.processInstanceId != process.id
&& !process.players.any { signal.sender.processInstanceId == it.id}
}
handler = { signal ->
players.add(PlayerInfo(
signal.data!!,
signal.sender.domain,
signal.sender.processInstanceId))
}
}
val waitPlayerOut = signalWait<String>("PlayerOut") { signal ->
players.remove(PlayerInfo(
signal.data!!,
signal.sender.domain,
signal.sender.processInstanceId))
allPlayersOut = players.isEmpty()
}
val sendHandshake = messageSend<String>("Handshake") {
messageData = { playerName }
activation = {
receiverDomain = process.players.last().domain
receiverProcessInstanceId = process.players.last().id
}
}
val receiveHandshake = messageWait<String>("Handshake") { message ->
if (!players.any { message.sender.processInstanceId == it.id}) {
players.add(PlayerInfo(
message.data!!,
message.sender.domain,
message.sender.processInstanceId))
}
}
val throwBall = messageSend<Int>("Ball") {
messageData = { shotCounter + 1 }
activation = { selectNextPlayer() }
onEntry { energy -= 1 }
}
val waitBall = messageWaitData<Int>("Ball") {
shotCounter = it
}
startFrom(waitNewGameSignal)
.fork("mainFork") {
next(sendPlayerJoin)
.branch("mainLoop") {
ifTrue { energy < 5 || allPlayersOut }
.next(sendPlayerOut)
.next(waitBall)
ifElse()
.next(waitBall)
.next(throwBall)
.loop()
}
next(waitPlayerJoin).next(sendHandshake).next(waitPlayerJoin)
next(waitPlayerOut).next(waitPlayerOut)
next(receiveHandshake).next(receiveHandshake)
next(waitStopGameSignal).terminate()
}
sendPlayerJoin.onExit { println("$playerName: I'm here!") }
sendPlayerOut.onExit { println("$playerName: I'm out!") }
}
private fun MessageSendInstance<RoundRobinPlayer, Int>.selectNextPlayer() {
var idx = process.nextPlayerIndex + 1
if (idx >= process.players.size) {
idx = 0
}
process.nextPlayerIndex = idx
val player = if (process.players.isNotEmpty())
process.players[idx]
else
process.selfPlayer
receiverDomain = player.domain
receiverProcessInstanceId = player.id
println("Step ${process.shotCounter + 1}: " +
"${process.playerName} >>> ${player.name}")
}
Aks holda, o'yinchining xatti-harakati avvalgisidan farq qilmaydi, shuning uchun diagramma o'zgarmaydi.
Endi hammasini ishga tushirish uchun test kerak. Maqola bilan bog'lanib qolmaslik uchun men faqat test kodini beraman (aslida men boshqa biznes-jarayonlarning integratsiyasini sinab ko'rish uchun avval yaratilgan test muhitidan foydalanganman):
Bularning barchasidan bir nechta muhim xulosalar chiqarish mumkin:
agar zarur vositalar mavjud bo'lsa, dastur ishlab chiquvchilari biznes mantig'idan uzoqlashmagan holda ilovalar o'rtasida integratsiya aloqalarini yaratishi mumkin;
muhandislik malakalarini talab qiladigan integratsiya vazifasining murakkabligi (murakkabligi), agar u dastlab ramka arxitekturasida belgilangan bo'lsa, ramka ichida yashirin bo'lishi mumkin. Vazifaning qiyinligini (qiyinchilikni) yashirib bo'lmaydi, shuning uchun koddagi qiyin vazifaning echimi shunga mos ravishda ko'rinadi;
integratsiya mantig'ini ishlab chiqishda, oxir-oqibat, barcha integratsiya ishtirokchilarining holatini o'zgartirishning izchilligi va chiziqli bo'lmasligini hisobga olish kerak. Bu bizni mantiqni tashqi hodisalar sodir bo'lish tartibiga befarq qilish uchun murakkablashtirishga majbur qiladi. Bizning misolimizda, o'yinchi o'yindan chiqqanini e'lon qilganidan keyin o'yinda qatnashishga majbur bo'ladi: uning chiqishi haqidagi ma'lumot yetib borguncha va barcha ishtirokchilar tomonidan qayta ishlanmaguncha boshqa o'yinchilar to'pni unga uzatishda davom etadilar. Bu mantiq o'yin qoidalaridan kelib chiqmaydi va tanlangan arxitektura doirasidagi murosali yechimdir.
Keyinchalik, yechimimizning turli nozikliklari, murosalar va boshqa fikrlar haqida gapiraylik.
Barcha xabarlar bitta navbatda
Barcha integratsiyalangan ilovalar tashqi broker sifatida taqdim etilgan bitta integratsiya avtobusi, xabarlar uchun bitta BPMQueue va signallar (hodisalar) uchun bitta BPMTopic mavzusi bilan ishlaydi. Barcha xabarlarni bitta navbat orqali o'tkazish o'z-o'zidan murosadir. Biznes mantig'i darajasida endi siz tizim tuzilishiga o'zgartirishlar kiritmasdan xohlagancha yangi turdagi xabarlarni kiritishingiz mumkin. Bu sezilarli soddalashtirish, lekin u bizning odatiy vazifalarimiz kontekstida bizga unchalik ahamiyatsiz bo'lib tuyulgan ma'lum xavflarni o'z ichiga oladi.
Biroq, bu erda bitta noziklik bor: har bir dastur kirishdagi navbatdan "o'z" xabarlarini o'z domeni nomi bilan filtrlaydi. Bundan tashqari, signalning "ko'lamini" bitta dastur bilan cheklash kerak bo'lsa, domen signallarda ko'rsatilishi mumkin. Bu avtobusning tarmoqli kengligini oshirishi kerak, ammo biznes mantig'i endi domen nomlari bilan ishlashi kerak: xabarlarni manzillash uchun majburiy, signallar uchun kerakli.
Integratsiya avtobusining ishonchliligini ta'minlash
Ishonchlilik bir necha omillardan iborat:
Tanlangan xabar brokeri arxitekturaning muhim komponenti va bitta nosozlik nuqtasidir: u xatoga etarlicha chidamli bo'lishi kerak. Siz yaxshi qo'llab-quvvatlash va katta hamjamiyat bilan faqat vaqt sinovidan o'tgan ilovalardan foydalanishingiz kerak;
xabar brokerining yuqori darajada mavjudligini ta'minlash kerak, buning uchun u jismonan integratsiyalangan ilovalardan ajratilishi kerak (qo'llaniladigan biznes mantig'iga ega ilovalarning yuqori mavjudligini ta'minlash ancha qiyin va qimmatroq);
broker "kamida bir marta" yetkazib berish kafolatlarini taqdim etishga majburdir. Bu integratsiya avtobusining ishonchli ishlashi uchun majburiy talabdir. "Aniq bir marta" darajadagi kafolatlarga ehtiyoj yo'q: biznes jarayonlari odatda xabarlar yoki hodisalarning takroriy kelishiga sezgir emas va bu muhim bo'lgan maxsus vazifalarda doimiy foydalanishdan ko'ra biznes mantig'iga qo'shimcha tekshiruvlar qo'shish osonroqdir. ancha "qimmat" "kafolatlar;
xabarlar va signallarni yuborish biznes jarayonlari va domen ma'lumotlarining holatini o'zgartirish bilan umumiy bitimda ishtirok etishi kerak. Eng maqbul variant naqshdan foydalanish bo'ladi Tranzaksiya chiqish qutisi, lekin u ma'lumotlar bazasida qo'shimcha jadval va o'rni talab qiladi. JEE ilovalarida buni mahalliy JTA menejeri yordamida soddalashtirish mumkin, ammo tanlangan brokerga ulanish rejimda ishlashi kerak. XA;
kiruvchi xabarlar va hodisalarni qayta ishlovchilar biznes-jarayon holatini o'zgartirish tranzaksiyasi bilan ham ishlashi kerak: agar bunday tranzaksiya orqaga qaytarilsa, xabarni qabul qilish ham bekor qilinishi kerak;
xatolar tufayli yetkazilmagan xabarlar alohida do'konda saqlanishi kerak D.L.Q. (O'lik xat navbati). Buning uchun biz bunday xabarlarni o'z xotirasida saqlaydigan, ularni atributlar bo'yicha indekslaydigan (tez guruhlash va qidirish uchun) va APIni ko'rish, manzil manziliga qayta yuborish va xabarlarni o'chirish uchun ochadigan alohida platforma mikroservis yaratdik. Tizim ma'murlari ushbu xizmat bilan o'zlarining veb-interfeysi orqali ishlashi mumkin;
broker sozlamalarida xabarlarning DLQ-ga tushish ehtimolini kamaytirish uchun etkazib berish urinishlari va etkazib berishlar orasidagi kechikishlar sonini sozlashingiz kerak (optimal parametrlarni hisoblash deyarli mumkin emas, lekin siz empirik tarzda harakat qilishingiz va ularni o'zgartirishingiz mumkin. operatsiya);
DLQ do'konini doimiy ravishda kuzatib borish kerak va monitoring tizimi tizim ma'murlarini xabardor qilishi kerak, shunda ular etkazib berilmagan xabarlar yuzaga kelganda imkon qadar tezroq javob berishlari mumkin. Bu nosozlik yoki biznes mantiqiy xatosining "zarar zonasini" kamaytiradi;
integratsiya avtobusi ilovalarning vaqtinchalik yo'qligiga befarq bo'lishi kerak: mavzu obunalari bardoshli bo'lishi kerak va ilovaning domen nomi noyob bo'lishi kerak, shunda boshqa birov ilova yo'qligida navbatdan uning xabarini qayta ishlashga harakat qilmaydi.
Biznes mantig'ining ip xavfsizligini ta'minlash
Biznes jarayonining bir xil nusxasi bir vaqtning o'zida bir nechta xabarlar va hodisalarni qabul qilishi mumkin, ularni qayta ishlash parallel ravishda boshlanadi. Shu bilan birga, dastur ishlab chiquvchisi uchun hamma narsa oddiy va iplar uchun xavfsiz bo'lishi kerak.
Jarayon biznes mantig'i ushbu biznes jarayoniga alohida ta'sir ko'rsatadigan har bir tashqi hodisani qayta ishlaydi. Bunday hodisalar bo'lishi mumkin:
biznes-jarayon namunasini ishga tushirish;
biznes jarayonidagi faoliyat bilan bog'liq foydalanuvchi harakati;
biznes-jarayon namunasi obuna bo'lgan xabar yoki signalni olish;
biznes jarayoni instantsiyasi tomonidan belgilangan taymerning tugashi;
API orqali harakatni boshqarish (masalan, jarayonni to'xtatish).
Har bir bunday hodisa biznes jarayonining holatini o'zgartirishi mumkin: ba'zi harakatlar tugashi va boshqalar boshlanishi mumkin, doimiy xususiyatlarning qiymatlari o'zgarishi mumkin. Har qanday faoliyatni yopish quyidagi faoliyatlardan biri yoki bir nechtasini faollashtirishga olib kelishi mumkin. Ular, o'z navbatida, boshqa hodisalarni kutishni to'xtatishlari mumkin yoki agar ularga qo'shimcha ma'lumotlar kerak bo'lmasa, ular xuddi shu tranzaksiyani bajarishlari mumkin. Tranzaktsiyani yopishdan oldin, biznes jarayonining yangi holati ma'lumotlar bazasida saqlanadi, u erda keyingi tashqi hodisani kutadi.
Relyatsion ma'lumotlar bazasida saqlanadigan doimiy biznes-jarayon ma'lumotlari SELECT FOR UPDATE-dan foydalanganda juda qulay ishlov berish sinxronizatsiya nuqtasidir. Agar bitta tranzaksiya ma'lumotlar bazasidan biznes-jarayon holatini uni o'zgartirish uchun olishga muvaffaq bo'lsa, parallel ravishda boshqa hech qanday operatsiya boshqa o'zgarish uchun xuddi shunday holatni ololmaydi va birinchi operatsiya tugagandan so'ng, ikkinchisi. allaqachon o'zgartirilgan holatni olish kafolatlangan.
DBMS tomonidagi pessimistik qulflardan foydalanib, biz barcha kerakli talablarni bajaramiz ACID, shuningdek, ishlaydigan misollar sonini ko'paytirish orqali dasturni biznes mantig'i bilan o'lchash qobiliyatini saqlab qoling.
Biroq, pessimistik qulflar bizni boshi berk ko'chaga olib kelishi bilan tahdid qiladi, ya'ni SELECT FOR UPDATE biznes mantig'idagi ba'zi dahshatli holatlarda boshi berk ko'chaga tushib qolgan taqdirda hamon ba'zi oqilona kutish vaqti bilan cheklanishi kerak.
Yana bir muammo - biznes jarayonining boshlanishini sinxronlashtirish. Biznes-jarayon namunasi bo'lmasa-da, ma'lumotlar bazasida ham holat yo'q, shuning uchun tasvirlangan usul ishlamaydi. Agar siz ma'lum bir sohada biznes-jarayon namunasining o'ziga xosligini ta'minlashni istasangiz, u holda sizga jarayon sinfi va mos keladigan doira bilan bog'liq bo'lgan sinxronizatsiya ob'ektining bir turi kerak bo'ladi. Ushbu muammoni hal qilish uchun biz tashqi xizmat orqali URI formatidagi kalit tomonidan ko'rsatilgan o'zboshimchalik manbasini blokirovka qilish imkonini beruvchi boshqa qulflash mexanizmidan foydalanamiz.
Bizning misollarimizda InitialPlayer biznes jarayoni deklaratsiyani o'z ichiga oladi
uniqueConstraint = UniqueConstraints.singleton
Shuning uchun jurnalda tegishli kalitning qulfini olish va bo'shatish haqidagi xabarlar mavjud. Boshqa biznes jarayonlar uchun bunday xabarlar yo'q: uniqueConstraint o'rnatilmagan.
Doimiy holat bilan biznes jarayoni muammolari
Ba'zida doimiy holatga ega bo'lish nafaqat yordam beradi, balki haqiqatan ham rivojlanishga to'sqinlik qiladi.
Muammolar biznes mantig'iga va/yoki biznes jarayoni modeliga o'zgartirish kiritishingiz kerak bo'lganda boshlanadi. Bunday o'zgarishlar biznes jarayonlarining eski holatiga mos kelmaydi. Agar ma'lumotlar bazasida juda ko'p "jonli" misollar mavjud bo'lsa, unda mos kelmaydigan o'zgarishlar jBPM-dan foydalanishda biz tez-tez duch keladigan ko'plab muammolarni keltirib chiqarishi mumkin.
O'zgarishlarning chuqurligiga qarab, siz ikkita usulda harakat qilishingiz mumkin:
eskisiga mos kelmaydigan o'zgartirishlar kiritmaslik uchun yangi biznes-jarayon turini yarating va yangi misollarni boshlashda uni eskisi o'rniga ishlating. Eski misollar "eski usulda" ishlashda davom etadi;
biznes mantig'ini yangilashda biznes jarayonlarining doimiy holatini ko'chiring.
Birinchi usul sodda, ammo uning cheklovlari va kamchiliklari bor, masalan:
biznes-jarayonlarning ko'plab modellarida biznes mantiqining takrorlanishi, biznes mantiqi hajmining oshishi;
tez-tez yangi biznes mantig'iga bir zumda o'tish talab qilinadi (deyarli har doim integratsiya vazifalari nuqtai nazaridan);
ishlab chiquvchi qaysi nuqtada eskirgan modellarni o'chirish mumkinligini bilmaydi.
Amalda biz ikkala yondashuvdan ham foydalanamiz, lekin hayotimizni soddalashtirish uchun bir qator qarorlar qabul qildik:
ma'lumotlar bazasida biznes jarayonining doimiy holati osongina o'qiladigan va oson qayta ishlanadigan shaklda saqlanadi: JSON formatidagi satrda. Bu sizga dastur ichida ham, tashqarisida ham migratsiyalarni amalga oshirish imkonini beradi. Haddan tashqari holatlarda siz uni tutqichlar bilan ham sozlashingiz mumkin (ayniqsa, disk raskadrovka paytida ishlab chiqishda foydali);
integratsiya biznes mantig'i biznes jarayonlarining nomlaridan foydalanmaydi, shuning uchun istalgan vaqtda ishtirok etuvchi jarayonlardan birini amalga oshirishni yangi nom bilan, yangi nom bilan almashtirish mumkin (masalan, "InitialPlayerV2"). Bog'lanish xabarlar va signallarning nomlari orqali sodir bo'ladi;
jarayon modeli versiya raqamiga ega, agar biz ushbu modelga mos kelmaydigan o'zgarishlar kiritsak, biz uni oshiramiz va bu raqam jarayon misolining holati bilan birga saqlanadi;
jarayonning doimiy holati birinchi navbatda bazadan qulay ob'ekt modeliga o'qiladi, agar modelning versiya raqami o'zgargan bo'lsa, migratsiya protsedurasi ishlashi mumkin;
migratsiya protsedurasi biznes mantig'i yonida joylashtiriladi va ma'lumotlar bazasidan qayta tiklanganda biznes jarayonining har bir misoli uchun "dangasa" deb nomlanadi;
agar siz barcha jarayon misollarining holatini tez va sinxron tarzda ko'chirishingiz kerak bo'lsa, ko'proq klassik ma'lumotlar bazasini ko'chirish echimlaridan foydalaniladi, lekin u erda JSON bilan ishlashingiz kerak.
Menga biznes jarayonlari uchun boshqa ramka kerakmi?
Maqolada tasvirlangan echimlar hayotimizni sezilarli darajada soddalashtirishga, ilovalarni ishlab chiqish darajasida hal qilinadigan muammolar doirasini kengaytirishga va biznes mantig'ini mikroservislarga ajratish g'oyasini yanada jozibador qilishga imkon berdi. Buning uchun juda ko'p ishlar amalga oshirildi, biznes jarayonlari uchun juda "engil" ramka, shuningdek, keng ko'lamli amaliy vazifalar kontekstida aniqlangan muammolarni hal qilish uchun xizmat ko'rsatish komponentlari yaratildi. Biz ushbu natijalarni baham ko'rish, umumiy komponentlarning rivojlanishini bepul litsenziya ostida ochiq kirishga olib kelish istagimiz bor. Bu biroz kuch va vaqt talab qiladi. Bunday yechimlarga bo'lgan talabni tushunish biz uchun qo'shimcha rag'bat bo'lishi mumkin. Taklif etilayotgan maqolada ramkaning imkoniyatlariga juda kam e'tibor beriladi, ammo ularning ba'zilari keltirilgan misollardan ko'rinadi. Agar biz shunga qaramay o'z ramkamizni nashr qilsak, unga alohida maqola bag'ishlanadi. Ayni paytda, savolga javob berib, ozgina fikr-mulohaza qoldirsangiz, minnatdor bo'lamiz:
So'rovda faqat ro'yxatdan o'tgan foydalanuvchilar ishtirok etishlari mumkin. tizimga kirishiltimos.
Menga biznes jarayonlari uchun boshqa ramka kerakmi?
18,8%Ha, men uzoq vaqtdan beri shunga o'xshash narsani qidiryapman.
12,5%amalga oshirish haqida ko'proq ma'lumot olish qiziq, bu foydali bo'lishi mumkin2
6,2%biz mavjud ramkalardan birini ishlatamiz, lekin biz uni almashtirish haqida o'ylaymiz1
18,8%biz mavjud ramkalardan birini ishlatamiz, hamma narsa mos keladi3
18,8%ramkasiz engish3
25,0%o'zingizni yozing 4
16 foydalanuvchi ovoz berdi. 7 nafar foydalanuvchi betaraf qolgan.