Ujumuishaji wa mtindo wa BPM

Ujumuishaji wa mtindo wa BPM

Halo, Habr!

Kampuni yetu inataalam katika ukuzaji wa suluhisho za programu za darasa la ERP, sehemu kubwa ambayo inamilikiwa na mifumo ya shughuli na idadi kubwa ya mantiki ya biashara na mtiririko wa hati la EDMS. Matoleo ya sasa ya bidhaa zetu yanatokana na teknolojia ya JavaEE, lakini pia tunajaribu kikamilifu huduma ndogo. Mojawapo ya maeneo yenye shida zaidi ya suluhisho kama hizo ni ujumuishaji wa mifumo ndogo ndogo ya vikoa vya karibu. Matatizo ya ujumuishaji daima yametupa maumivu ya kichwa, bila kujali mitindo ya usanifu, safu za teknolojia na mifumo tunayotumia, lakini hivi karibuni kumekuwa na maendeleo katika kutatua matatizo hayo.

Katika makala ninayokuletea, nitazungumzia kuhusu uzoefu na utafiti wa usanifu ambao NPO "Krista" ina katika eneo lililochaguliwa. Pia tutaangalia mfano wa suluhisho rahisi kwa tatizo la ushirikiano kutoka kwa mtazamo wa msanidi programu na kujua ni nini kilichofichwa nyuma ya unyenyekevu huu.

Kanusho

Ufumbuzi wa usanifu na kiufundi ulioelezwa katika makala unapendekezwa na mimi kulingana na uzoefu wa kibinafsi katika mazingira ya kazi maalum. Suluhu hizi hazidai kuwa za ulimwengu wote na zinaweza zisiwe bora chini ya hali zingine za matumizi.

BPM ina uhusiano gani nayo?

Ili kujibu swali hili, tunahitaji kutafakari kwa kina kidogo juu ya maalum ya matatizo yaliyotumiwa ya ufumbuzi wetu. Sehemu kuu ya mantiki ya biashara katika mfumo wetu wa kawaida wa shughuli ni kuingiza data kwenye hifadhidata kupitia miingiliano ya watumiaji, uthibitishaji wa mwongozo na kiotomatiki wa data hii, kuifanya kupitia utiririshaji fulani wa kazi, kuichapisha kwa mfumo mwingine / hifadhidata ya uchanganuzi / kumbukumbu, kutoa ripoti. . Kwa hivyo, kazi muhimu ya mfumo kwa wateja ni otomatiki ya michakato yao ya ndani ya biashara.

Kwa urahisi, tunatumia neno "hati" katika mawasiliano kama uondoaji fulani wa seti ya data iliyounganishwa na ufunguo wa kawaida ambao mtiririko fulani wa kazi unaweza "kuunganishwa".
Lakini vipi kuhusu mantiki ya ujumuishaji? Baada ya yote, kazi ya ujumuishaji hutolewa na usanifu wa mfumo, ambao "hukatwa" katika sehemu SI kwa ombi la mteja, lakini chini ya ushawishi wa mambo tofauti kabisa:

  • chini ya sheria ya Conway;
  • kama matokeo ya kutumia tena mifumo ndogo iliyotengenezwa hapo awali kwa bidhaa zingine;
  • kwa hiari ya mbunifu, kwa kuzingatia mahitaji yasiyo ya kazi.

Kuna jaribu kubwa la kutenganisha mantiki ya ujumuishaji kutoka kwa mantiki ya biashara ya mtiririko mkuu wa kazi, ili usichafue mantiki ya biashara na vizalia vya ujumuishaji na kumwondolea msanidi programu kutoka kwa hitaji la kupekua katika maelezo mahususi ya mazingira ya usanifu wa mfumo. Njia hii ina faida kadhaa, lakini mazoezi yanaonyesha kutofaa kwake:

  • kutatua matatizo ya ushirikiano kwa kawaida huanguka kwa chaguo rahisi zaidi kwa namna ya simu za synchronous kutokana na pointi ndogo za upanuzi katika utekelezaji wa kazi kuu ya kazi (hasara za ushirikiano wa synchronous zinajadiliwa hapa chini);
  • vizalia vya programu vya ujumuishaji bado vinapenya mantiki ya msingi ya biashara wakati maoni kutoka kwa mfumo mwingine mdogo yanahitajika;
  • msanidi programu hupuuza ujumuishaji na anaweza kuivunja kwa urahisi kwa kubadilisha mtiririko wa kazi;
  • mfumo huacha kuwa nzima kutoka kwa mtazamo wa mtumiaji, "seams" kati ya mifumo ndogo huonekana, na uendeshaji wa ziada wa mtumiaji huonekana, kuanzisha uhamisho wa data kutoka kwa mfumo mdogo hadi mwingine.

Mbinu nyingine ni kuzingatia mwingiliano wa ujumuishaji kama sehemu muhimu ya mantiki ya msingi ya biashara na mtiririko wa kazi. Ili kuzuia sifa za msanidi programu kutoka kwa kasi, kuunda mwingiliano mpya wa ujumuishaji unapaswa kuwa rahisi na usio na nguvu, na fursa ndogo ya kuchagua suluhisho. Hii ni vigumu kufanya kuliko inavyoonekana: chombo lazima kiwe na nguvu ya kutosha kumpa mtumiaji chaguo mbalimbali zinazohitajika kwa matumizi yake, bila kumruhusu "kujipiga risasi kwenye mguu." Kuna maswali mengi ambayo mhandisi lazima ajibu katika muktadha wa kazi za ujumuishaji, lakini ambayo msanidi programu haipaswi kufikiria katika kazi yake ya kila siku: mipaka ya shughuli, uthabiti, atomiki, usalama, kuongeza, mzigo na usambazaji wa rasilimali, uelekezaji, upangaji, usambazaji na miktadha ya kubadili, n.k. Ni muhimu kuwapa wasanidi programu violezo vya suluhisho rahisi ambavyo majibu ya maswali kama haya tayari yamefichwa. Violezo hivi lazima ziwe salama kabisa: mantiki ya biashara hubadilika mara nyingi sana, ambayo huongeza hatari ya kuanzisha makosa, gharama ya makosa lazima ibaki katika kiwango cha chini kabisa.

Lakini BPM ina uhusiano gani nayo? Kuna chaguzi nyingi za kutekeleza mtiririko wa kazi ...
Hakika, utekelezaji mwingine wa michakato ya biashara ni maarufu sana katika ufumbuzi wetu - kupitia ufafanuzi wa kutangaza wa mchoro wa mpito wa serikali na uunganisho wa washughulikiaji na mantiki ya biashara kwa mabadiliko. Katika kesi hiyo, hali ambayo huamua nafasi ya sasa ya "hati" katika mchakato wa biashara ni sifa ya "hati" yenyewe.

Ujumuishaji wa mtindo wa BPM
Hivi ndivyo mchakato unavyoonekana mwanzoni mwa mradi

Umaarufu wa utekelezaji huu ni kwa sababu ya unyenyekevu wa jamaa na kasi ya kuunda michakato ya biashara ya mstari. Hata hivyo, kadiri mifumo ya programu inavyozidi kuwa ngumu zaidi, sehemu ya otomatiki ya mchakato wa biashara inakua na kuwa ngumu zaidi. Kuna haja ya kuoza, kutumia tena sehemu za michakato, pamoja na michakato ya matawi ili kila tawi litekelezwe sambamba. Chini ya hali kama hizi, chombo kinakuwa kibaya, na mchoro wa mpito wa serikali hupoteza yaliyomo kwenye habari (mwingiliano wa ujumuishaji hauonyeshwa kwenye mchoro hata kidogo).

Ujumuishaji wa mtindo wa BPM
Hivi ndivyo mchakato unavyoonekana baada ya marudio kadhaa ya ufafanuzi wa mahitaji.

Njia ya nje ya hali hii ilikuwa ushirikiano wa injini jBPM katika baadhi ya bidhaa zilizo na michakato ngumu zaidi ya biashara. Kwa muda mfupi, suluhisho hili lilikuwa na mafanikio fulani: iliwezekana kutekeleza michakato ngumu ya biashara wakati wa kudumisha mchoro unaofaa na unaofaa katika nukuu. BPMN2.

Ujumuishaji wa mtindo wa BPM
Sehemu ndogo ya mchakato mgumu wa biashara

Kwa muda mrefu, suluhisho halikufikia matarajio: nguvu ya juu ya kazi ya kuunda michakato ya biashara kupitia zana za kuona haikuruhusu kufikia viashiria vya tija vinavyokubalika, na chombo yenyewe ikawa mojawapo ya wasiopenda zaidi kati ya watengenezaji. Pia kulikuwa na malalamiko juu ya muundo wa ndani wa injini, ambayo ilisababisha kuonekana kwa "patches" nyingi na "magongo".

Kipengele kikuu chanya cha kutumia jBPM kilikuwa ufahamu wa manufaa na madhara ya kuwa na hali ya kudumu ya mfano wa mchakato wa biashara. Pia tuliona uwezekano wa kutumia mbinu ya mchakato kutekeleza itifaki tata za ujumuishaji kati ya programu tofauti kwa kutumia mwingiliano wa asynchronous kupitia ishara na ujumbe. Uwepo wa hali ya kudumu ina jukumu muhimu katika hili.

Kulingana na hapo juu, tunaweza kuhitimisha: Mtazamo wa mchakato katika mtindo wa BPM huturuhusu kutatua kazi nyingi zaidi za kubinafsisha michakato ya biashara inayozidi kuwa ngumu, kutosheleza shughuli za ujumuishaji katika michakato hii na kudumisha uwezo wa kuonyesha mchakato unaotekelezwa kwa nukuu inayofaa.

Hasara za simu zinazolingana kama muundo wa ujumuishaji

Ujumuishaji wa usawazishaji hurejelea simu rahisi zaidi ya kuzuia. Mfumo mdogo mmoja hufanya kazi kama upande wa seva na kufichua API kwa njia inayohitajika. Mfumo mwingine mdogo hufanya kama upande wa mteja na kwa wakati unaofaa hupiga simu na kungoja matokeo. Kulingana na usanifu wa mfumo, pande za mteja na seva zinaweza kupatikana katika programu na mchakato sawa, au kwa tofauti. Katika kesi ya pili, unahitaji kutumia baadhi ya utekelezaji wa RPC na kutoa marshalling ya vigezo na matokeo ya simu.

Ujumuishaji wa mtindo wa BPM

Mchoro huu wa ujumuishaji una seti kubwa ya hasara, lakini hutumiwa sana katika mazoezi kwa sababu ya unyenyekevu wake. Kasi ya utekelezaji inakuvutia na kukulazimisha kuitumia tena na tena licha ya tarehe za mwisho, kurekodi suluhisho kama deni la kiufundi. Lakini pia hutokea kwamba watengenezaji wasio na ujuzi hutumia bila kujua, bila kutambua matokeo mabaya.

Mbali na ongezeko la wazi zaidi la uunganisho wa mfumo mdogo, pia kuna matatizo ya chini ya wazi na shughuli za "kukua" na "kunyoosha". Hakika, ikiwa mantiki ya biashara inafanya mabadiliko fulani, basi shughuli haziwezi kuepukwa, na shughuli, kwa upande wake, huzuia rasilimali fulani za maombi zilizoathiriwa na mabadiliko haya. Hiyo ni, hadi mfumo mdogo usubiri jibu kutoka kwa mwingine, hautaweza kukamilisha shughuli na kuondoa kufuli. Hii kwa kiasi kikubwa huongeza hatari ya madhara mbalimbali:

  • Mwitikio wa mfumo umepotea, watumiaji wanasubiri kwa muda mrefu majibu ya maswali;
  • seva kwa ujumla huacha kujibu maombi ya mtumiaji kwa sababu ya mkusanyiko wa nyuzi nyingi: nyuzi nyingi zimefungwa kwenye rasilimali inayochukuliwa na shughuli;
  • Deadlocks huanza kuonekana: uwezekano wa kutokea kwao hutegemea sana muda wa shughuli, kiasi cha mantiki ya biashara na kufuli zinazohusika katika shughuli;
  • hitilafu za kuisha kwa shughuli zinaonekana;
  • seva "inashindwa" na OutOfMemory ikiwa kazi inahitaji usindikaji na kubadilisha kiasi kikubwa cha data, na kuwepo kwa ushirikiano wa synchronous hufanya iwe vigumu sana kugawanya usindikaji katika shughuli "nyepesi".

Kutoka kwa mtazamo wa usanifu, matumizi ya kuzuia simu wakati wa ushirikiano husababisha kupoteza udhibiti wa ubora wa mfumo mdogo wa mtu binafsi: haiwezekani kuhakikisha viashiria vya ubora wa lengo la mfumo mdogo kwa kutengwa na viashiria vya ubora wa mfumo mwingine mdogo. Ikiwa mifumo ndogo itatengenezwa na timu tofauti, hii ni shida kubwa.

Mambo yanapendeza zaidi ikiwa mifumo ndogo inayounganishwa iko katika programu tofauti na unahitaji kufanya mabadiliko ya usawaziko kwa pande zote mbili. Jinsi ya kuhakikisha shughuli za mabadiliko haya?

Ikiwa mabadiliko yanafanywa katika shughuli tofauti, basi utahitaji kutoa utunzaji wa ubaguzi wa kuaminika na fidia, na hii inaondoa kabisa faida kuu ya ushirikiano wa synchronous - unyenyekevu.

Shughuli zinazosambazwa pia zinakuja akilini, lakini hatuzitumii katika suluhisho zetu: ni ngumu kuhakikisha kuegemea.

"Saga" kama suluhisho la tatizo la muamala

Pamoja na umaarufu unaokua wa huduma ndogo, mahitaji ya Mfano wa Saga.

Mtindo huu hutatua kikamilifu matatizo yaliyotajwa hapo juu ya shughuli za muda mrefu, na pia huongeza uwezo wa kusimamia hali ya mfumo kutoka upande wa mantiki ya biashara: fidia baada ya shughuli iliyoshindwa haiwezi kurejesha mfumo kwa hali yake ya awali, lakini kutoa. njia mbadala ya usindikaji wa data. Hii pia hukuruhusu kuzuia kurudia hatua zilizokamilishwa za uchakataji wa data unapojaribu kuleta mchakato huo kwenye mwisho "mzuri".

Inafurahisha, katika mifumo ya monolithic muundo huu pia ni muhimu linapokuja suala la ujumuishaji wa mifumo ndogo iliyounganishwa kwa urahisi na athari mbaya zinazosababishwa na shughuli za muda mrefu na kufuli za rasilimali zinazolingana huzingatiwa.

Kuhusiana na michakato yetu ya biashara katika mtindo wa BPM, inageuka kuwa rahisi sana kutekeleza "Sagas": hatua za kibinafsi za "Saga" zinaweza kubainishwa kama shughuli ndani ya mchakato wa biashara, na hali inayoendelea ya mchakato wa biashara pia. huamua hali ya ndani ya "Saga". Hiyo ni, hatuhitaji utaratibu wowote wa ziada wa uratibu. Unachohitaji ni wakala wa ujumbe ambaye anaauni dhamana ya "angalau mara moja" kama usafiri.

Lakini suluhisho hili pia lina "bei" yake mwenyewe:

  • mantiki ya biashara inakuwa ngumu zaidi: fidia inahitaji kufanyiwa kazi;
  • itakuwa muhimu kuacha msimamo kamili, ambayo inaweza kuwa nyeti hasa kwa mifumo ya monolithic;
  • Usanifu unakuwa ngumu zaidi, na haja ya ziada ya broker ya ujumbe inaonekana;
  • zana za ziada za ufuatiliaji na utawala zitahitajika (ingawa kwa ujumla hii ni nzuri: ubora wa huduma ya mfumo utaongezeka).

Kwa mifumo ya monolithic, uhalali wa kutumia "Sag" sio dhahiri sana. Kwa huduma ndogo na SOA zingine, ambapo uwezekano mkubwa tayari kuna wakala, na uthabiti kamili hutolewa mwanzoni mwa mradi, faida za kutumia muundo huu zinaweza kuzidi ubaya, haswa ikiwa kuna API inayofaa katika mantiki ya biashara. kiwango.

Kuingiza mantiki ya biashara katika huduma ndogo ndogo

Tulipoanza kufanya majaribio na huduma ndogo, swali la busara liliibuka: wapi kuweka mantiki ya biashara ya kikoa kuhusiana na huduma ambayo inahakikisha kuendelea kwa data ya kikoa?

Wakati wa kuangalia usanifu wa BPMS mbalimbali, inaweza kuonekana kuwa sawa kutenganisha mantiki ya biashara na kuendelea: kuunda safu ya jukwaa na huduma ndogo zinazojitegemea za kikoa ambazo zinaunda mazingira na chombo cha kutekeleza mantiki ya biashara ya kikoa, na kubuni kuendelea kwa data ya kikoa kama safu tofauti ya microservices rahisi sana na nyepesi. Michakato ya biashara katika kesi hii hufanya orchestration ya huduma za safu ya kuendelea.

Ujumuishaji wa mtindo wa BPM

Njia hii ina faida kubwa sana: unaweza kuongeza utendaji wa jukwaa kama unavyopenda, na safu inayolingana tu ya huduma ndogo za jukwaa itakuwa "mafuta" kutoka kwa hii. Michakato ya biashara kutoka kwa kikoa chochote inaweza mara moja kutumia utendakazi mpya wa jukwaa mara tu inaposasishwa.

Uchunguzi wa kina zaidi ulifunua hasara kubwa za mbinu hii:

  • huduma ya jukwaa inayotekeleza mantiki ya biashara ya vikoa vingi mara moja hubeba hatari kubwa kama hatua moja ya kushindwa. Mabadiliko ya mara kwa mara kwa mantiki ya biashara huongeza hatari ya makosa na kusababisha kushindwa kwa mfumo mzima;
  • masuala ya utendaji: mantiki ya biashara hufanya kazi na data yake kupitia kiolesura finyu na polepole:
    • data itakuwa mara nyingine tena marshalled na pumped kupitia mtandao stack;
    • huduma ya kikoa mara nyingi itatoa data zaidi kuliko inavyotakiwa kwa mantiki ya biashara kuchakatwa kwa sababu ya kutokuwa na uwezo wa kutosha wa kuainisha maombi katika kiwango cha API ya nje ya huduma;
    • vipande kadhaa vya kujitegemea vya mantiki ya biashara vinaweza kuomba tena data sawa kwa ajili ya usindikaji (tatizo hili linaweza kupunguzwa kwa kuongeza vipengele vya kikao vinavyohifadhi data, lakini hii inachanganya zaidi usanifu na kuunda matatizo ya umuhimu wa data na ubatilifu wa cache);
  • matatizo ya muamala:
    • michakato ya biashara na hali inayoendelea, ambayo huhifadhiwa na huduma ya jukwaa, haiendani na data ya kikoa, na hakuna njia rahisi za kutatua tatizo hili;
    • kuweka kizuizi cha data ya kikoa nje ya shughuli: ikiwa mantiki ya biashara ya kikoa inahitaji kufanya mabadiliko baada ya kwanza kuangalia usahihi wa data ya sasa, ni muhimu kuwatenga uwezekano wa mabadiliko ya ushindani katika data iliyochakatwa. Kuzuia data ya nje kunaweza kusaidia kutatua tatizo, lakini ufumbuzi huo hubeba hatari za ziada na hupunguza uaminifu wa jumla wa mfumo;
  • matatizo ya ziada wakati wa kusasisha: katika baadhi ya matukio, huduma ya kudumu na mantiki ya biashara inahitaji kusasishwa kwa usawa au kwa mlolongo mkali.

Hatimaye, ilitubidi kurudi kwenye misingi: kujumuisha data ya kikoa na mantiki ya biashara ya kikoa kwenye huduma ndogo ndogo. Njia hii hurahisisha mtazamo wa huduma ndogo kama sehemu muhimu ya mfumo na haitoi shida zilizo hapo juu. Hii pia haijatolewa bure:

  • Usanifu wa API unahitajika kwa mwingiliano na mantiki ya biashara (haswa, kutoa shughuli za mtumiaji kama sehemu ya michakato ya biashara) na huduma za jukwaa la API; inahitaji uangalifu zaidi kwa mabadiliko ya API, utangamano wa mbele na nyuma;
  • inahitajika kuongeza maktaba za ziada za wakati wa kukimbia ili kuhakikisha utendakazi wa mantiki ya biashara kama sehemu ya kila huduma ndogo kama hiyo, na hii inatoa mahitaji mapya ya maktaba kama hizo: wepesi na kiwango cha chini cha utegemezi wa mpito;
  • watengenezaji wa mantiki ya biashara wanahitaji kufuatilia matoleo ya maktaba: ikiwa huduma ndogo haijakamilishwa kwa muda mrefu, basi kuna uwezekano mkubwa kuwa na toleo la zamani la maktaba. Hiki kinaweza kuwa kikwazo kisichotarajiwa cha kuongeza kipengele kipya na kinaweza kuhitaji kuhamisha mantiki ya zamani ya biashara ya huduma kama hiyo hadi kwa matoleo mapya ya maktaba ikiwa kulikuwa na mabadiliko yasiyooanishwa kati ya matoleo.

Ujumuishaji wa mtindo wa BPM

Safu ya huduma za jukwaa pia iko katika usanifu huo, lakini safu hii haifanyi tena chombo cha kutekeleza mantiki ya biashara ya kikoa, lakini tu mazingira yake, kutoa kazi za "jukwaa" za ziada. Safu kama hiyo inahitajika sio tu kudumisha asili nyepesi ya huduma ndogo za kikoa, lakini pia kuweka usimamizi kati.

Kwa mfano, shughuli za mtumiaji katika michakato ya biashara huzalisha kazi. Hata hivyo, wakati wa kufanya kazi na kazi, mtumiaji lazima aone kazi kutoka kwa vikoa vyote katika orodha ya jumla, ambayo ina maana lazima kuwe na huduma ya usajili wa kazi ya jukwaa inayolingana, kufutwa kwa mantiki ya biashara ya kikoa. Kudumisha encapsulation ya mantiki ya biashara katika muktadha kama huo ni shida kabisa, na hii ni maelewano mengine ya usanifu huu.

Ujumuishaji wa michakato ya biashara kupitia macho ya msanidi programu

Kama ilivyoelezwa hapo juu, msanidi programu lazima atolewe kutoka kwa sifa za kiufundi na za uhandisi za kutekeleza mwingiliano wa programu kadhaa ili mtu ategemee tija nzuri ya maendeleo.

Wacha tujaribu kusuluhisha shida ngumu ya ujumuishaji, iliyoundwa mahsusi kwa kifungu hicho. Hii itakuwa kazi ya "mchezo" inayohusisha programu tatu, ambapo kila mmoja wao anafafanua jina fulani la kikoa: "app1", "app2", "app3".

Ndani ya kila programu, michakato ya biashara inazinduliwa ambayo huanza "kucheza mpira" kupitia basi ya ujumuishaji. Ujumbe wenye jina "Mpira" utafanya kama mpira.

Kanuni za mchezo:

  • mchezaji wa kwanza ni mwanzilishi. Anawaalika wachezaji wengine kwenye mchezo, anaanza mchezo na anaweza kuumaliza wakati wowote;
  • wachezaji wengine wanatangaza ushiriki wao katika mchezo, "kujua" kila mmoja na mchezaji wa kwanza;
  • baada ya kupokea mpira, mchezaji huchagua mchezaji mwingine anayeshiriki na kupitisha mpira kwake. Jumla ya idadi ya maambukizi imehesabiwa;
  • Kila mchezaji ana "nishati" ambayo hupungua kwa kila pasi ya mpira na mchezaji huyo. Wakati nishati inapoisha, mchezaji huacha mchezo, akitangaza kujiuzulu kwake;
  • ikiwa mchezaji ameachwa peke yake, mara moja anatangaza kuondoka kwake;
  • Wakati wachezaji wote wameondolewa, mchezaji wa kwanza anatangaza mchezo umekwisha. Iwapo atatoka mapema kwenye mchezo, anabaki kuufuatilia mchezo ili kuukamilisha.

Ili kutatua tatizo hili, nitatumia DSL yetu kwa michakato ya biashara, ambayo inaruhusu sisi kuelezea mantiki katika Kotlin kwa ukamilifu, na kiwango cha chini cha boilerplate.

Mchakato wa biashara wa mchezaji wa kwanza (aliyejulikana pia kama mwanzilishi wa mchezo) utafanya kazi katika programu1:

darasa AwaliPlayer

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}")
}

Mbali na kutekeleza mantiki ya biashara, nambari iliyo hapo juu inaweza kutoa kielelezo cha kitu cha mchakato wa biashara, ambacho kinaweza kuonyeshwa kwa namna ya mchoro. Bado hatujatekeleza taswira, kwa hivyo ilitubidi kutumia muda kidogo kuichora (hapa nimerahisisha nukuu ya BPMN kuhusu utumiaji wa milango ili kuboresha uthabiti wa mchoro na nambari uliyopewa):

Ujumuishaji wa mtindo wa BPM

app2 itajumuisha mchakato wa biashara wa mchezaji mwingine:

darasa RandomPlayer

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}")
}

Mchoro:

Ujumuishaji wa mtindo wa BPM

Katika programu ya app3 tutafanya mchezaji mwenye tabia tofauti kidogo: badala ya kuchagua kwa nasibu mchezaji anayefuata, atachukua hatua kulingana na kanuni ya mzunguko wa robin:

darasa 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}")
}

Vinginevyo, tabia ya mchezaji haina tofauti na ya awali, hivyo mchoro haubadilika.

Sasa tunahitaji mtihani ili kuendesha haya yote. Nitatoa tu msimbo wa mtihani yenyewe, ili usiingie makala na boilerplate (kwa kweli, nilitumia mazingira ya mtihani yaliyoundwa mapema ili kupima ushirikiano wa michakato mingine ya biashara):

testGame()

@Test
public void testGame() throws InterruptedException {
    String pl2 = startProcess(app2, "RandomPlayer", playerParams("Player2", 20));
    String pl3 = startProcess(app2, "RandomPlayer", playerParams("Player3", 40));
    String pl4 = startProcess(app3, "RoundRobinPlayer", playerParams("Player4", 25));
    String pl5 = startProcess(app3, "RoundRobinPlayer", playerParams("Player5", 35));
    String pl1 = startProcess(app1, "InitialPlayer");
    // Теперь нужно немного подождать, пока игроки "познакомятся" друг с другом.
    // Ждать через sleep - плохое решение, зато самое простое. 
    // Не делайте так в серьезных тестах!
    Thread.sleep(1000);
    // Запускаем игру, закрывая пользовательскую активность
    assertTrue(closeTask(app1, pl1, "Start"));
    app1.getWaiting().waitProcessFinished(pl1);
    app2.getWaiting().waitProcessFinished(pl2);
    app2.getWaiting().waitProcessFinished(pl3);
    app3.getWaiting().waitProcessFinished(pl4);
    app3.getWaiting().waitProcessFinished(pl5);
}

private Map<String, Object> playerParams(String name, int energy) {
    Map<String, Object> params = new HashMap<>();
    params.put("playerName", name);
    params.put("energy", energy);
    return params;
}

Wacha tufanye jaribio na tuangalie logi:

pato la console

Взята блокировка ключа lock://app1/process/InitialPlayer
Let's play!
Снята блокировка ключа lock://app1/process/InitialPlayer
Player2: I'm here!
Player3: I'm here!
Player4: I'm here!
Player5: I'm here!
... join player Player2 ...
... join player Player4 ...
... join player Player3 ...
... join player Player5 ...
Step 1: Player1 >>> Player3
Step 2: Player3 >>> Player5
Step 3: Player5 >>> Player3
Step 4: Player3 >>> Player4
Step 5: Player4 >>> Player3
Step 6: Player3 >>> Player4
Step 7: Player4 >>> Player5
Step 8: Player5 >>> Player2
Step 9: Player2 >>> Player5
Step 10: Player5 >>> Player4
Step 11: Player4 >>> Player2
Step 12: Player2 >>> Player4
Step 13: Player4 >>> Player1
Step 14: Player1 >>> Player4
Step 15: Player4 >>> Player3
Step 16: Player3 >>> Player1
Step 17: Player1 >>> Player2
Step 18: Player2 >>> Player3
Step 19: Player3 >>> Player1
Step 20: Player1 >>> Player5
Step 21: Player5 >>> Player1
Step 22: Player1 >>> Player2
Step 23: Player2 >>> Player4
Step 24: Player4 >>> Player5
Step 25: Player5 >>> Player3
Step 26: Player3 >>> Player4
Step 27: Player4 >>> Player2
Step 28: Player2 >>> Player5
Step 29: Player5 >>> Player2
Step 30: Player2 >>> Player1
Step 31: Player1 >>> Player3
Step 32: Player3 >>> Player4
Step 33: Player4 >>> Player1
Step 34: Player1 >>> Player3
Step 35: Player3 >>> Player4
Step 36: Player4 >>> Player3
Step 37: Player3 >>> Player2
Step 38: Player2 >>> Player5
Step 39: Player5 >>> Player4
Step 40: Player4 >>> Player5
Step 41: Player5 >>> Player1
Step 42: Player1 >>> Player5
Step 43: Player5 >>> Player3
Step 44: Player3 >>> Player5
Step 45: Player5 >>> Player2
Step 46: Player2 >>> Player3
Step 47: Player3 >>> Player2
Step 48: Player2 >>> Player5
Step 49: Player5 >>> Player4
Step 50: Player4 >>> Player2
Step 51: Player2 >>> Player5
Step 52: Player5 >>> Player1
Step 53: Player1 >>> Player5
Step 54: Player5 >>> Player3
Step 55: Player3 >>> Player5
Step 56: Player5 >>> Player2
Step 57: Player2 >>> Player1
Step 58: Player1 >>> Player4
Step 59: Player4 >>> Player1
Step 60: Player1 >>> Player4
Step 61: Player4 >>> Player3
Step 62: Player3 >>> Player2
Step 63: Player2 >>> Player5
Step 64: Player5 >>> Player4
Step 65: Player4 >>> Player5
Step 66: Player5 >>> Player1
Step 67: Player1 >>> Player5
Step 68: Player5 >>> Player3
Step 69: Player3 >>> Player4
Step 70: Player4 >>> Player2
Step 71: Player2 >>> Player5
Step 72: Player5 >>> Player2
Step 73: Player2 >>> Player1
Step 74: Player1 >>> Player4
Step 75: Player4 >>> Player1
Step 76: Player1 >>> Player2
Step 77: Player2 >>> Player5
Step 78: Player5 >>> Player4
Step 79: Player4 >>> Player3
Step 80: Player3 >>> Player1
Step 81: Player1 >>> Player5
Step 82: Player5 >>> Player1
Step 83: Player1 >>> Player4
Step 84: Player4 >>> Player5
Step 85: Player5 >>> Player3
Step 86: Player3 >>> Player5
Step 87: Player5 >>> Player2
Step 88: Player2 >>> Player3
Player2: I'm out!
Step 89: Player3 >>> Player4
... player Player2 is out ...
Step 90: Player4 >>> Player1
Step 91: Player1 >>> Player3
Step 92: Player3 >>> Player1
Step 93: Player1 >>> Player4
Step 94: Player4 >>> Player3
Step 95: Player3 >>> Player5
Step 96: Player5 >>> Player1
Step 97: Player1 >>> Player5
Step 98: Player5 >>> Player3
Step 99: Player3 >>> Player5
Step 100: Player5 >>> Player4
Step 101: Player4 >>> Player5
Player4: I'm out!
... player Player4 is out ...
Step 102: Player5 >>> Player1
Step 103: Player1 >>> Player3
Step 104: Player3 >>> Player1
Step 105: Player1 >>> Player3
Step 106: Player3 >>> Player5
Step 107: Player5 >>> Player3
Step 108: Player3 >>> Player1
Step 109: Player1 >>> Player3
Step 110: Player3 >>> Player5
Step 111: Player5 >>> Player1
Step 112: Player1 >>> Player3
Step 113: Player3 >>> Player5
Step 114: Player5 >>> Player3
Step 115: Player3 >>> Player1
Step 116: Player1 >>> Player3
Step 117: Player3 >>> Player5
Step 118: Player5 >>> Player1
Step 119: Player1 >>> Player3
Step 120: Player3 >>> Player5
Step 121: Player5 >>> Player3
Player5: I'm out!
... player Player5 is out ...
Step 122: Player3 >>> Player5
Step 123: Player5 >>> Player1
Player5: I'm out!
Step 124: Player1 >>> Player3
... player Player5 is out ...
Step 125: Player3 >>> Player1
Step 126: Player1 >>> Player3
Player1: I'm out!
... player Player1 is out ...
Step 127: Player3 >>> Player3
Player3: I'm out!
Step 128: Player3 >>> Player3
... player Player3 is out ...
Player3: I'm out!
Stop!
Step 129: Player3 >>> Player3
Player3: I'm out!

Kutoka kwa haya yote tunaweza kupata hitimisho kadhaa muhimu:

  • na zana zinazohitajika, watengenezaji wa programu wanaweza kuunda mwingiliano wa ujumuishaji kati ya programu bila kukatiza mantiki ya biashara;
  • utata wa kazi ya ujumuishaji ambayo inahitaji ustadi wa uhandisi inaweza kufichwa ndani ya mfumo ikiwa hii imejumuishwa hapo awali katika usanifu wa mfumo. Ugumu wa shida hauwezi kufichwa, kwa hivyo suluhisho la shida ngumu katika nambari itaonekana kama hiyo;
  • Wakati wa kuunda mantiki ya ujumuishaji, ni muhimu kuzingatia uthabiti wa mwisho na ukosefu wa usawa wa mabadiliko katika hali ya washiriki wote wa ujumuishaji. Hii inatulazimisha kutatiza mantiki ili kuifanya isijali kwa mpangilio ambao matukio ya nje hutokea. Kwa mfano wetu, mchezaji analazimika kushiriki katika mchezo baada ya kutangaza kuondoka kwake kutoka kwa mchezo: wachezaji wengine wataendelea kumpa mpira hadi habari kuhusu kuondoka kwake ifikie na kushughulikiwa na washiriki wote. Mantiki hii haifuati kutoka kwa sheria za mchezo na ni suluhisho la maelewano ndani ya mfumo wa usanifu uliochaguliwa.

Ifuatayo, tutazungumza juu ya ugumu mbalimbali wa suluhisho letu, maelewano na vidokezo vingine.

Barua pepe zote ziko kwenye foleni moja

Programu zote zilizounganishwa hufanya kazi na basi moja ya ujumuishaji, ambayo inawasilishwa kwa njia ya wakala wa nje, BPMQueue moja ya ujumbe na mada moja ya BPMTopic kwa ishara (matukio). Kuweka ujumbe wote kupitia foleni moja yenyewe ni maelewano. Katika kiwango cha mantiki ya biashara, sasa unaweza kutambulisha aina nyingi mpya za ujumbe upendavyo bila kufanya mabadiliko kwenye muundo wa mfumo. Huu ni kurahisisha muhimu, lakini hubeba hatari fulani, ambazo katika muktadha wa kazi zetu za kawaida hazikuonekana kuwa muhimu sana kwetu.

Ujumuishaji wa mtindo wa BPM

Walakini, kuna ujanja mmoja hapa: kila programu huchuja ujumbe "wake" kutoka kwa foleni kwenye mlango, kwa jina la kikoa chake. Kikoa kinaweza pia kubainishwa katika ishara ikiwa unahitaji kuweka kikomo "wigo wa mwonekano" wa mawimbi kwa programu moja tu. Hii inapaswa kuongeza mtiririko wa basi, lakini mantiki ya biashara lazima sasa ifanye kazi na majina ya kikoa: kwa kushughulikia ujumbe - lazima, kwa ishara - zinazohitajika.

Kuhakikisha Uaminifu wa Basi la Ushirikiano

Kuegemea kuna mambo kadhaa:

  • Broker ya ujumbe iliyochaguliwa ni sehemu muhimu ya usanifu na hatua moja ya kushindwa: lazima iwe na uvumilivu wa kutosha wa makosa. Unapaswa kutumia utekelezaji uliojaribiwa kwa muda tu, kwa usaidizi mzuri na jumuiya kubwa;
  • inahitajika kuhakikisha upatikanaji wa juu wa wakala wa ujumbe, ambao lazima utenganishwe na programu zilizojumuishwa (upatikanaji wa juu wa programu zilizo na mantiki ya biashara iliyotumika ni ngumu zaidi na ni ghali kuhakikisha);
  • wakala analazimika kutoa dhamana ya utoaji "angalau mara moja". Hii ni mahitaji ya lazima kwa uendeshaji wa kuaminika wa basi ya ushirikiano. Hakuna haja ya dhamana ya kiwango cha "mara moja": michakato ya biashara, kama sheria, sio nyeti kwa kuwasili mara kwa mara kwa ujumbe au matukio, na katika kazi maalum ambapo hii ni muhimu, ni rahisi kuongeza ukaguzi wa ziada kwa biashara. mantiki kuliko kutumia kila mara dhamana "ghali" ";
  • kutuma ujumbe na ishara lazima kuhusishwe katika shughuli ya jumla na mabadiliko katika hali ya michakato ya biashara na data ya kikoa. Chaguo bora itakuwa kutumia muundo Kikasha Toezi cha Shughuli, lakini itahitaji jedwali la ziada kwenye hifadhidata na mrudiaji. Katika programu za JEE, hii inaweza kurahisishwa kwa kutumia meneja wa ndani wa JTA, lakini muunganisho kwa wakala aliyechaguliwa lazima uweze kufanya kazi ndani. XA;
  • washughulikiaji wa ujumbe unaoingia na matukio lazima pia wafanye kazi na shughuli inayobadilisha hali ya mchakato wa biashara: ikiwa shughuli hiyo imerejeshwa, basi upokeaji wa ujumbe lazima ughairiwe;
  • ujumbe ambao haukuweza kuwasilishwa kwa sababu ya hitilafu lazima uhifadhiwe katika hifadhi tofauti D.L.Q. (Foleni ya Barua zilizokufa). Kwa kusudi hili, tuliunda huduma ndogo ya jukwaa tofauti ambayo huhifadhi ujumbe kama huo katika hifadhi yake, kuorodhesha kulingana na sifa (kwa upangaji wa vikundi na utafutaji wa haraka), na kufichua API ya kutazamwa, kutuma tena kwa anwani lengwa, na kufuta ujumbe. Wasimamizi wa mfumo wanaweza kufanya kazi na huduma hii kupitia kiolesura chao cha wavuti;
  • katika mipangilio ya wakala, unahitaji kurekebisha idadi ya majaribio na ucheleweshaji wa uwasilishaji kati ya uwasilishaji ili kupunguza uwezekano wa ujumbe kuingia kwenye DLQ (karibu haiwezekani kuhesabu vigezo bora, lakini unaweza kuchukua hatua kwa nguvu na kuzirekebisha wakati wa operesheni. );
  • Hifadhi ya DLQ lazima ifuatiliwe mara kwa mara, na mfumo wa ufuatiliaji lazima uonye wasimamizi wa mfumo ili wakati ujumbe ambao haujawasilishwa unatokea, waweze kujibu haraka iwezekanavyo. Hii itapunguza "eneo lililoathiriwa" la kushindwa au kosa la mantiki ya biashara;
  • basi la ujumuishaji lazima lisiwe na hisia kwa kukosekana kwa maombi kwa muda: usajili kwa mada lazima uwe wa kudumu, na jina la kikoa la programu lazima liwe la kipekee ili wakati maombi hayapo, mtu mwingine hatajaribu kushughulikia ujumbe wake kutoka kwa foleni.

Kuhakikisha usalama wa thread ya mantiki ya biashara

Mfano huo wa mchakato wa biashara unaweza kupokea ujumbe na matukio kadhaa mara moja, usindikaji ambao utaanza sambamba. Wakati huo huo, kwa msanidi programu, kila kitu kinapaswa kuwa rahisi na salama.

Mantiki ya biashara ya mchakato huchakata kila tukio la nje linaloathiri mchakato huo wa biashara mmoja mmoja. Matukio kama haya yanaweza kuwa:

  • kuzindua mfano wa mchakato wa biashara;
  • hatua ya mtumiaji inayohusiana na shughuli ndani ya mchakato wa biashara;
  • kupokea ujumbe au ishara ambayo tukio la mchakato wa biashara limesajiliwa;
  • kuchochea kwa timer iliyowekwa na mfano wa mchakato wa biashara;
  • kudhibiti kitendo kupitia API (kwa mfano, kukatizwa kwa mchakato).

Kila tukio kama hilo linaweza kubadilisha hali ya mchakato wa biashara: baadhi ya shughuli zinaweza kuisha na zingine kuanza, na maadili ya sifa zinazoendelea zinaweza kubadilika. Kufunga shughuli yoyote kunaweza kusababisha kuwezesha moja au zaidi ya shughuli zifuatazo. Wale, kwa upande wake, wanaweza kuacha kusubiri matukio mengine au, ikiwa hawana haja ya data yoyote ya ziada, wanaweza kukamilisha katika shughuli hiyo hiyo. Kabla ya kufunga shughuli, hali mpya ya mchakato wa biashara huhifadhiwa kwenye hifadhidata, ambapo itasubiri tukio la nje la pili kutokea.

Data endelevu ya mchakato wa biashara iliyohifadhiwa katika hifadhidata ya uhusiano ni hatua rahisi sana ya kusawazisha uchakataji ukitumia CHAGUA KWA USASISHAJI. Ikiwa shughuli moja imeweza kupata hali ya mchakato wa biashara kutoka kwa msingi wa kuibadilisha, basi hakuna shughuli nyingine sambamba itaweza kupata hali sawa kwa mabadiliko mengine, na baada ya kukamilika kwa shughuli ya kwanza, ya pili ni. umehakikishiwa kupokea hali ambayo tayari imebadilishwa.

Kwa kutumia kufuli zisizo na matumaini upande wa DBMS, tunatimiza mahitaji yote muhimu ACID, na pia kuhifadhi uwezo wa kuongeza programu kwa mantiki ya biashara kwa kuongeza idadi ya matukio yanayoendeshwa.

Hata hivyo, kufuli zisizo na matumaini hututishia kwa kufuli, ambayo ina maana kwamba CHAGUA KWA USASISHAJI bado inapaswa kuwa na muda unaofaa wa kuisha iwapo mikwamo itatokea kwa baadhi ya matukio mabaya katika mantiki ya biashara.

Shida nyingine ni maingiliano ya kuanza kwa mchakato wa biashara. Ingawa hakuna mfano wa mchakato wa biashara, hakuna hali katika hifadhidata, kwa hivyo njia iliyoelezewa haitafanya kazi. Ikiwa unahitaji kuhakikisha upekee wa mfano wa mchakato wa biashara katika wigo maalum, basi utahitaji aina fulani ya kitu cha maingiliano kinachohusishwa na darasa la mchakato na upeo unaolingana. Ili kutatua tatizo hili, tunatumia mbinu tofauti ya kufunga ambayo huturuhusu kuchukua kufuli kwenye rasilimali kiholela iliyobainishwa na ufunguo katika umbizo la URI kupitia huduma ya nje.

Katika mifano yetu, mchakato wa biashara wa InitialPlayer una tamko

uniqueConstraint = UniqueConstraints.singleton

Kwa hiyo, logi ina ujumbe kuhusu kuchukua na kutolewa lock ya ufunguo unaofanana. Hakuna jumbe kama hizo kwa michakato mingine ya biashara: uniqueConstraint haijawekwa.

Shida za michakato ya biashara na hali inayoendelea

Wakati mwingine kuwa na hali inayoendelea sio tu inasaidia, lakini pia huzuia maendeleo.
Matatizo huanza wakati mabadiliko yanahitajika kufanywa kwa mantiki ya biashara na/au mtindo wa mchakato wa biashara. Sio kila mabadiliko kama haya yanaendana na hali ya zamani ya michakato ya biashara. Ikiwa kuna matukio mengi ya moja kwa moja kwenye hifadhidata, basi kufanya mabadiliko yasiyolingana kunaweza kusababisha shida nyingi, ambazo mara nyingi tulikutana nazo wakati wa kutumia jBPM.

Kulingana na kina cha mabadiliko, unaweza kutenda kwa njia mbili:

  1. unda aina mpya ya mchakato wa biashara ili usifanye mabadiliko yasiokubaliana kwa ya zamani, na uitumie badala ya ya zamani wakati wa kuzindua matukio mapya. Nakala za zamani zitaendelea kufanya kazi "kama hapo awali";
  2. hamisha hali inayoendelea ya michakato ya biashara wakati wa kusasisha mantiki ya biashara.

Njia ya kwanza ni rahisi, lakini ina mapungufu na hasara zake, kwa mfano:

  • kurudia kwa mantiki ya biashara katika mifano mingi ya mchakato wa biashara, kuongeza kiasi cha mantiki ya biashara;
  • Mara nyingi mpito wa haraka kwa mantiki mpya ya biashara inahitajika (kwa suala la kazi za ujumuishaji - karibu kila wakati);
  • msanidi programu hajui ni wakati gani mifano ya kizamani inaweza kufutwa.

Kwa mazoezi tunatumia njia zote mbili, lakini tumefanya maamuzi kadhaa ili kurahisisha maisha yetu:

  • Katika hifadhidata, hali inayoendelea ya mchakato wa biashara huhifadhiwa katika fomu inayosomeka kwa urahisi na kuchakatwa kwa urahisi: katika mfuatano wa umbizo la JSON. Hii inaruhusu uhamishaji kufanywa ndani ya programu na nje. Kama suluhisho la mwisho, unaweza kusahihisha kwa mikono (haswa muhimu katika ukuzaji wakati wa kurekebisha hitilafu);
  • mantiki ya biashara ya ujumuishaji haitumii majina ya michakato ya biashara, ili wakati wowote inawezekana kuchukua nafasi ya utekelezaji wa moja ya michakato inayoshiriki na mpya na jina jipya (kwa mfano, "InitialPlayerV2"). Kufunga hufanyika kupitia majina ya ujumbe na ishara;
  • muundo wa mchakato una nambari ya toleo, ambayo tunaongeza ikiwa tutafanya mabadiliko yasiyolingana kwa mfano huu, na nambari hii imehifadhiwa pamoja na hali ya mfano wa mchakato;
  • hali ya kuendelea ya mchakato inasomwa kutoka kwa hifadhidata kwanza hadi kwa mfano wa kitu rahisi, ambacho utaratibu wa uhamiaji unaweza kufanya kazi ikiwa nambari ya toleo la mfano imebadilika;
  • utaratibu wa uhamiaji umewekwa karibu na mantiki ya biashara na inaitwa "wavivu" kwa kila tukio la mchakato wa biashara wakati wa kurejeshwa kwake kutoka kwa hifadhidata;
  • ikiwa unahitaji kuhamisha hali ya matukio yote ya mchakato haraka na kwa usawazishaji, suluhu za uhamishaji wa hifadhidata zaidi hutumiwa, lakini lazima ufanye kazi na JSON.

Je, unahitaji mfumo mwingine wa michakato ya biashara?

Suluhisho zilizoelezewa katika kifungu zilituruhusu kurahisisha maisha yetu, kupanua anuwai ya maswala yaliyotatuliwa katika kiwango cha ukuzaji wa programu, na kufanya wazo la kutenganisha mantiki ya biashara kuwa huduma ndogo kuvutia zaidi. Ili kufikia hili, kazi nyingi zilifanyika, mfumo wa "nyepesi" sana wa michakato ya biashara uliundwa, pamoja na vipengele vya huduma ili kutatua matatizo yaliyotambuliwa katika mazingira ya matatizo mbalimbali ya maombi. Tuna hamu ya kushiriki matokeo haya na kufanya uundaji wa vipengele vya kawaida ufikie wazi chini ya leseni ya bila malipo. Hii itahitaji juhudi na wakati fulani. Kuelewa mahitaji ya suluhu kama hizo kunaweza kuwa kichocheo cha ziada kwetu. Katika makala iliyopendekezwa, tahadhari ndogo sana hulipwa kwa uwezo wa mfumo yenyewe, lakini baadhi yao yanaonekana kutokana na mifano iliyotolewa. Ikiwa tutachapisha mfumo wetu, nakala tofauti itatolewa kwake. Wakati huo huo, tutashukuru ikiwa utaacha maoni kidogo kwa kujibu swali:

Watumiaji waliojiandikisha pekee ndio wanaweza kushiriki katika utafiti. Weka sahihitafadhali.

Je, unahitaji mfumo mwingine wa michakato ya biashara?

  • 18,8%Ndio, nimekuwa nikitafuta kitu kama hiki kwa muda mrefu

  • 12,5%Nina nia ya kujifunza zaidi juu ya utekelezaji wako, inaweza kuwa muhimu2

  • 6,2%Tunatumia mojawapo ya mifumo iliyopo, lakini tunafikiria kuhusu kubadilisha1

  • 18,8%Tunatumia moja ya mifumo iliyopo, kila kitu kiko sawa3

  • 18,8%tunasimamia bila mfumo3

  • 25,0%andika yako4

Watumiaji 16 walipiga kura. Watumiaji 7 walijizuia.

Chanzo: mapenzi.com

Kuongeza maoni