Integrasi gaya BPM

Integrasi gaya BPM

Halo, Habr!

Pausahaan kami ngahususkeun kana pamekaran solusi parangkat lunak kelas ERP, dimana bagian singa dijajah ku sistem transaksional kalayan jumlah logika bisnis sareng alur kerja a la EDMS. Versi modérn produk kami dumasar kana téknologi JavaEE, tapi kami ogé aktip ékspérimén sareng microservices. Salah sahiji daérah anu paling bermasalah tina solusi sapertos kitu nyaéta integrasi sababaraha subsistem anu aya hubunganana sareng domain anu caket. Tugas integrasi sok masihan urang nyeri sirah anu ageung, henteu paduli gaya arsitéktur, tumpukan téknologi sareng kerangka anu kami anggo, tapi nembe aya kamajuan dina ngarengsekeun masalah sapertos kitu.

Dina artikel dibawa ka perhatian Anjeun, kuring bakal ngobrol ngeunaan pangalaman jeung panalungtikan arsitéktur NPO Krista di wewengkon ditunjuk. Urang ogé bakal mertimbangkeun conto solusi saderhana pikeun masalah integrasi tina sudut pandang pamekar aplikasi sareng milarian naon anu disumputkeun di tukangeun kesederhanaan ieu.

Bantahan

Solusi arsitéktur sareng téknis anu dijelaskeun dina tulisan éta ditawarkeun ku kuring dumasar kana pangalaman pribadi dina konteks tugas khusus. Solusi ieu henteu ngaku universal sareng tiasa henteu optimal dina kaayaan pamakean anu sanés.

Naon hubunganana BPM sareng éta?

Pikeun ngajawab patarosan ieu, urang kudu delve saeutik kana spésifik tina masalah dilarapkeun solusi urang. Bagian utama logika bisnis dina sistem transactional has urang nyaeta asupna data kana database ngaliwatan interfaces pamaké, manual tur otomatis verifikasi data ieu, ngaliwatan eta ngaliwatan sababaraha workflow, medarkeun ka sistem sejen / database analitik / arsip, ngahasilkeun laporan. Ku kituna, fungsi konci sistem pikeun konsumén nyaéta automation prosés bisnis internal maranéhanana.

Pikeun genah, kami nganggo istilah "dokumen" dina komunikasi salaku sababaraha abstraksi set data, dihijikeun ku konci umum, nu a workflow husus bisa "napel".
Tapi kumaha upami logika integrasi? Barina ogé, tugas integrasi dihasilkeun ku arsitéktur sistem, nu "sawed" kana bagian NOT di paménta konsumén, tapi dina pangaruh faktor lengkep béda:

  • dina pangaruh hukum Conway;
  • salaku hasil tina pamakéan deui subsistem saméméhna dimekarkeun pikeun produk lianna;
  • sakumaha diputuskeun ku arsiték, dumasar kana sarat non-fungsi.

Aya godaan anu hébat pikeun misahkeun logika integrasi tina logika bisnis alur kerja utama supados henteu ngotoran logika bisnis sareng artefak integrasi sareng ngahemat pamekar aplikasi tina kedah ngagali kana peculiarities bentang arsitéktur sistem. Pendekatan ieu ngagaduhan sababaraha kaunggulan, tapi prakték nunjukkeun inefisiensina:

  • ngarengsekeun masalah integrasi biasana slides handap ka pilihan pangbasajanna dina bentuk panggero sinkron alatan titik extension kawates dina palaksanaan workflow utama (langkung lengkep ihwal shortcomings integrasi sinkron handap);
  • artefak integrasi masih nembus logika bisnis utama nalika eupan balik ti subsistem séjén diperlukeun;
  • pamekar aplikasi teu malire integrasi jeung bisa kalayan gampang megatkeun eta ku cara ngarobah workflow nu;
  • Sistim nu ceases janten sakabeh tunggal ti sudut pandang pamaké, "seams" antara subsistem jadi noticeable, operasi pamaké kaleuleuwihan muncul nu initiate mindahkeun data ti hiji subsistem ka nu sejen.

Pendekatan anu sanés nyaéta mertimbangkeun interaksi integrasi salaku bagian integral tina logika bisnis inti sareng alur kerja. Pikeun ngajaga sarat kaahlian pamekar aplikasi tina skyrocketing, nyieun interaksi integrasi anyar kudu dipigawé gampang jeung alami, kalawan pilihan minimal keur milih solusi. Ieu langkung hese tibatan anu katingalina: alatna kedah cukup kuat pikeun nyayogikeun pangguna rupa-rupa pilihan anu dipikabutuh pikeun dianggo sareng dina waktos anu sami henteu ngantepkeun diri ditembak dina suku. Aya seueur patarosan anu kedah dijawab ku insinyur dina kontéks tugas integrasi, tapi anu henteu kedah dipikirkeun ku pamekar aplikasi dina padamelan sapopoé: wates transaksi, konsistensi, atomicity, kaamanan, skala, beban sareng distribusi sumberdaya, routing, marshaling, rambatan jeung switching konteks, jsb Ieu diperlukeun pikeun nawarkeun pamekar aplikasi cukup basajan kaputusan template, nu waleran kana sagala patarosan misalna geus disumputkeun. Pola ieu kedah cukup aman: logika bisnis sering pisan robih, anu ningkatkeun résiko ngenalkeun kasalahan, biaya kasalahan kedah tetep dina tingkat anu cukup rendah.

Tapi tetep, naon hubunganana BPM sareng éta? Aya seueur pilihan pikeun ngalaksanakeun alur kerja ...
Mémang, palaksanaan prosés bisnis anu sanés populer pisan dina solusi kami - ngalangkungan setting deklaratif tina diagram transisi kaayaan sareng nyambungkeun pawang sareng logika bisnis kana transisi. Dina waktos anu sami, kaayaan anu nangtukeun posisi ayeuna "dokumen" dina prosés bisnis mangrupikeun atribut tina "dokumen" sorangan.

Integrasi gaya BPM
Ieu kumaha prosésna sigana di mimiti proyek

Popularitas palaksanaan sapertos kitu kusabab kesederhanaan relatif sareng laju nyiptakeun prosés bisnis linier. Nanging, nalika sistem parangkat lunak janten langkung kompleks, bagian otomatis tina prosés bisnis tumbuh sareng janten langkung kompleks. Aya anu peryogi pikeun dékomposisi, nganggo deui bagéan prosés, ogé prosés forking supados unggal cabang dieksekusi paralel. Dina kaayaan kitu, alat jadi teu merenah, sarta diagram transisi kaayaan leungiteun eusi informasi na (interaksi integrasi teu reflected dina diagram).

Integrasi gaya BPM
Ieu kumaha prosésna sigana saatos sababaraha iterasi pikeun ngajelaskeun sarat

Jalan kaluar tina kaayaan ieu integrasi mesin jBPM kana sababaraha produk sareng prosés bisnis anu paling kompleks. Dina jangka pondok, solusi ieu ngagaduhan sababaraha kasuksésan: janten mungkin pikeun ngalaksanakeun prosés bisnis anu rumit bari ngajaga diagram anu cukup informatif sareng up-to-date dina notasi. BPMN2.

Integrasi gaya BPM
Hiji bagian leutik tina prosés bisnis kompléks

Dina jangka panjang, leyuran teu hirup nepi ka ekspektasi: inténsitas kuli tinggi nyieun prosés bisnis ngaliwatan parabot visual teu ngidinan achieving indikator produktivitas ditarima, sarta alat sorangan janten salah sahiji anu pang disliked diantara pamekar. Aya ogé keluhan ngeunaan struktur internal mesin, nu ngarah ka penampilan loba "patches" jeung "crutches".

Aspék positip utama tina ngagunakeun jBPM nyaéta realisasi mangpaat sareng cilaka tina gaduh kaayaan pengkuh sorangan pikeun conto prosés bisnis. Urang ogé nempo kamungkinan ngagunakeun pendekatan prosés pikeun nerapkeun protokol integrasi kompléks antara aplikasi béda ngagunakeun interaksi Asynchronous ngaliwatan sinyal jeung pesen. Ayana kaayaan pengkuh maénkeun peran krusial dina ieu.

Dumasar kana hal di luhur, urang tiasa nyimpulkeun: Pendekatan prosés dina gaya BPM ngamungkinkeun urang pikeun ngabéréskeun rupa-rupa pancén pikeun ngajadikeun otomatis prosés bisnis anu langkung rumit, sacara harmonis nyocogkeun kagiatan integrasi kana prosés ieu sareng nahan kamampuan pikeun ningalikeun sacara visual prosés anu dilaksanakeun dina notasi anu cocog.

Kakurangan telepon sinkron salaku pola integrasi

Integrasi sinkron nujul kana panggero blocking pangbasajanna. Hiji subsistem tindakan minangka sisi server sareng ngalaan API sareng metode anu dipikahoyong. Subsistem séjén tindakan salaku sisi klien tur, dina waktos katuhu, nelepon jeung ekspektasi hasilna. Gumantung kana arsitéktur sistem, sisi klien sareng server tiasa di-host dina aplikasi sareng prosés anu sami, atanapi dina anu béda. Dina kasus kadua, Anjeun kudu nerapkeun sababaraha palaksanaan RPC sarta nyadiakeun marshalling sahiji parameter jeung hasil panggero.

Integrasi gaya BPM

Pola integrasi sapertos kitu ngagaduhan set kalemahan anu cukup ageung, tapi seueur pisan dianggo dina prakték kusabab kesederhanaanna. Laju palaksanaan captivates jeung ngajadikeun anjeun nerapkeun eta deui jeung deui dina kaayaan "ngaduruk" deadlines, nulis solusi kana hutang teknis. Tapi éta ogé kajadian yén pamekar inexperienced ngagunakeun éta teu sadar, ngan saukur teu sadar konsékuansi négatip.

Salian kanaékan paling atra dina konektipitas subsistem, aya masalah kirang atra kalawan "nyebarkeun" jeung "manjang" transaksi. Mémang, upami logika bisnis ngajantenkeun parobihan, maka transaksi penting pisan, sareng transaksi, kahareupna ngonci sumber aplikasi anu tangtu anu kapangaruhan ku parobihan ieu. Nyaéta, dugi ka hiji subsistem ngantosan réspon ti anu sanés, éta moal tiasa ngalengkepan transaksi sareng ngaleupaskeun konci. Ieu sacara signifikan ningkatkeun résiko tina rupa-rupa épék:

  • responsiveness sistem leungit, pamaké antosan lila pikeun waleran kana requests;
  • server umumna eureun ngarespon kana requests pamaké alatan hiji kolam renang thread overflowing: lolobana threads "nangtung" dina konci sumberdaya dikawasaan ku urus;
  • deadlocks mimiti muncul: probabiliti lumangsungna maranéhna pisan gumantung kana durasi transaksi, jumlah logika bisnis jeung konci aub dina urus;
  • kasalahan béakna waktu urus némbongan;
  • server "ragrag" on OutOfMemory lamun tugas merlukeun ngolah sarta ngarobah jumlah badag data, sarta ayana integrations sinkron ngajadikeun hésé pisan dibeulah processing kana transaksi "torek".

Tina sudut pandang arsitéktur, panggunaan blokir sauran salami integrasi nyababkeun leungitna kontrol kualitas subsistem individu: mustahil pikeun mastikeun target kualitas hiji subsistem diisolasi tina target kualitas subsistem anu sanés. Upami subsistem dikembangkeun ku tim anu béda, ieu mangrupikeun masalah anu ageung.

Hal-hal janten langkung narik upami subsistem anu diintegrasikeun aya dina aplikasi anu béda sareng perobahan sinkron kedah dilakukeun dina dua sisi. Kumaha carana sangkan parobahan ieu transactional?

Upami parobihan dilakukeun dina transaksi anu kapisah, maka penanganan pengecualian anu kuat sareng santunan kedah disayogikeun, sareng ieu ngaleungitkeun kauntungan utama integrasi sinkron - kesederhanaan.

Transaksi anu disebarkeun ogé aya dina pikiran, tapi kami henteu nganggo éta dina solusi kami: hese pikeun mastikeun réliabilitas.

"Saga" salaku solusi pikeun masalah transaksi

Kalayan popularitas jasa mikro anu ningkat, aya paménta anu ningkat Pola Saga.

Pola ieu sampurna ngarengsekeun masalah di luhur tina transaksi anu panjang, sareng ogé ngalegaan kamungkinan ngatur kaayaan sistem tina sisi logika bisnis: santunan saatos transaksi anu gagal henteu tiasa ngagulung deui sistem ka kaayaan aslina, tapi nyayogikeun alternatif. jalur ngolah data. Éta ogé ngamungkinkeun anjeun henteu ngulang léngkah-léngkah ngolah data anu suksés nalika anjeun nyobian ngalaksanakeun prosésna ka tungtung anu "alus".

Narikna, dina sistem monolithic, pola ieu ogé relevan lamun datang ka integrasi subsistem gandeng bébas tur aya éfék négatif disababkeun ku transaksi lila jeung konci sumberdaya pakait.

Ngeunaan prosés bisnis urang dina gaya BPM, tétéla gampang pisan pikeun nerapkeun Sagas: léngkah individu Sagas tiasa disetél salaku kagiatan dina prosés bisnis, sareng kaayaan pengkuh tina prosés bisnis nangtukeun, antara séjén. , kaayaan internal tina Sagas. Nyaéta, urang henteu peryogi mékanisme koordinasi tambahan. Sadaya anu anjeun peryogikeun nyaéta calo pesen kalayan dukungan pikeun "sahenteuna sakali" jaminan salaku angkutan.

Tapi solusi sapertos kitu ogé ngagaduhan "harga" sorangan:

  • logika bisnis jadi leuwih kompleks: Anjeun kedah dianggo kaluar santunan;
  • eta bakal diperlukeun pikeun abandon konsistensi pinuh, nu tiasa utamana sénsitip pikeun sistem monolithic;
  • arsitéktur jadi saeutik leuwih pajeulit, aya hiji kabutuhan tambahan pikeun calo pesen;
  • alat ngawaskeun sareng administrasi tambahan bakal diperyogikeun (sanaos sacara umum ieu malah saé: kualitas layanan sistem bakal ningkat).

Pikeun sistem monolithic, leresan pikeun ngagunakeun "Sags" teu jadi atra. Pikeun microservices na SOAs sejen, dimana, paling dipikaresep, geus aya calo, sarta konsistensi pinuh dikurbankeun dina mimiti proyek, mangpaat ngagunakeun pola ieu nyata bisa outweigh kalemahan, utamana lamun aya hiji API merenah dina tingkat logika bisnis.

Encapsulation logika bisnis dina microservices

Nalika urang mimiti ékspérimén sareng microservices, timbul patarosan anu lumrah: dimana nempatkeun logika bisnis domain dina hubungan sareng jasa anu nyayogikeun data domain kegigihan?

Nalika ningali arsitektur tina rupa-rupa BPMS, sigana wajar pikeun misahkeun logika bisnis tina kegigihan: nyiptakeun lapisan platform sareng microservices mandiri domain anu ngabentuk lingkungan sareng wadah pikeun ngalaksanakeun logika bisnis domain, sareng ngatur kegigihan data domain salaku misah. lapisan microservices basajan pisan jeung lightweight. Prosés bisnis dina hal ieu orchestrate jasa lapisan kegigihan.

Integrasi gaya BPM

Pendekatan ieu ngagaduhan tambah anu ageung: anjeun tiasa ningkatkeun pungsionalitas platform saloba anjeun resep, sareng ngan ukur lapisan microservices platform anu saluyu anu bakal "gemuk" tina ieu. Prosés bisnis tina domain mana waé langsung kéngingkeun kasempetan pikeun ngagunakeun pungsionalitas platform énggal saatos diropéa.

Panaliti anu langkung rinci ngungkabkeun kakurangan anu signifikan tina pendekatan ieu:

  • jasa platform nu executes logika bisnis loba domain sakaligus mawa resiko hébat salaku titik tunggal gagal. Parobihan sering kana logika bisnis ningkatkeun résiko bug anu nyababkeun kagagalan di sakumna sistem;
  • masalah kinerja: logika bisnis jalan kalawan data na ngaliwatan panganteur sempit tur slow:
    • data bakal sakali deui marshaled sarta ngompa ngaliwatan tumpukan jaringan;
    • ladenan domain mindeng bakal balik deui data ti logika bisnis merlukeun pikeun ngolah, alatan kamampuhan parameterization query cukup dina tingkat API éksternal ladenan;
    • sababaraha lembar bebas logika bisnis bisa sababaraha kali ulang menta data anu sarua pikeun ngolah (anjeun tiasa mitigate masalah ieu ku nambahkeun kacang sési nu cache data, tapi ieu salajengna complicates arsitéktur sarta nyieun masalah freshness data na invalidation cache);
  • masalah transaksional:
    • prosés bisnis jeung kaayaan pengkuh disimpen ku layanan platform anu inconsistent jeung data domain, sarta teu aya cara gampang pikeun ngajawab masalah ieu;
    • mindahkeun konci data domain kaluar tina urus: lamun logika bisnis domain perlu nyieun parobahan, sanggeus mimiti mariksa correctness tina data sabenerna, perlu ngaluarkeun kamungkinan parobahan kalapa dina data olahan. Blok data éksternal tiasa ngabantosan ngarengsekeun masalah, tapi solusi sapertos kitu ngagaduhan résiko tambahan sareng ngirangan réliabilitas sistem;
  • komplikasi tambahan nalika ngamutahirkeun: dina sababaraha kasus, Anjeun kudu ngamutahirkeun ladenan kegigihan jeung logika bisnis sinkron atawa dina runtuyan ketat.

Tungtungna, kuring kudu balik deui ka dasar: encapsulate data domain jeung logika bisnis domain kana hiji microservice. Pendekatan ieu nyederhanakeun persepsi microservice salaku komponén integral dina sistem sareng henteu nyababkeun masalah di luhur. Ieu ogé henteu gratis:

  • Standarisasi API diperyogikeun pikeun interaksi sareng logika bisnis (khususna, pikeun nyayogikeun kagiatan pangguna salaku bagian tina prosés bisnis) sareng jasa platform API; perhatian leuwih ati kana parobahan API, kasaluyuan maju jeung mundur diperlukeun;
  • eta diperlukeun pikeun nambahkeun perpustakaan runtime tambahan pikeun mastikeun fungsi tina logika bisnis salaku bagian tina unggal microservice sapertos, sarta ieu ngabalukarkeun sarat anyar pikeun perpustakaan sapertos: lightness sarta minimum kagumantungan transitive;
  • pamekar logika bisnis kudu ngalacak versi perpustakaan: lamun microservice teu acan diadopsi keur lila, mangka paling dipikaresep ngandung versi luntur tina perpustakaan. Ieu tiasa janten halangan anu teu disangka-sangka pikeun nambihan fitur énggal sareng tiasa meryogikeun logika bisnis lami tina jasa sapertos kitu pikeun migrasi ka vérsi perpustakaan énggal upami aya parobahan anu teu cocog antara versi.

Integrasi gaya BPM

Lapisan jasa platform ogé aya dina arsitéktur sapertos kitu, tapi lapisan ieu henteu deui ngabentuk wadah pikeun ngalaksanakeun logika bisnis domain, tapi ngan ukur lingkunganana, nyayogikeun fungsi "platform" bantu. Lapisan sapertos kitu diperlukeun henteu ngan pikeun ngajaga lightness tina microservices domain, tapi ogé pikeun centralize manajemén.

Salaku conto, kagiatan pangguna dina prosés bisnis ngahasilkeun tugas. Nanging, nalika damel sareng tugas, pangguna kedah ningali tugas ti sadaya domain dina daptar umum, anu hartosna kedah aya jasa platform pendaptaran tugas anu pas, diberesihan tina logika bisnis domain. Ngajaga encapsulation logika bisnis dina konteks ieu rada masalah, sarta ieu kompromi sejen tina arsitektur ieu.

Integrasi prosés bisnis ngaliwatan mata pamekar aplikasi

Sakumaha anu parantos disebatkeun di luhur, pamekar aplikasi kedah diabstraksi tina fitur téknis sareng rékayasa palaksanaan interaksi sababaraha aplikasi supados tiasa ngandelkeun produktivitas pangembangan anu saé.

Hayu urang cobaan pikeun ngajawab masalah integrasi rada hese, husus nimukeun artikel. Ieu bakal tugas "kaulinan" ngalibetkeun tilu aplikasi, dimana masing-masing ngahartikeun sababaraha ngaran domain: "app1", "app2", "app3".

Di jero unggal aplikasi, prosés bisnis diluncurkeun anu mimiti "maén bal" ngaliwatan beus integrasi. Pesen ngaranna "Ball" bakal meta salaku bal.

Aturan kaulinan:

  • pamuter kahiji nyaeta inisiator nu. Anjeunna Ujang pamaén séjén pikeun kaulinan, dimimitian kaulinan sarta bisa mungkas eta iraha wae;
  • pamaén séjén nyatakeun partisipasi maranéhanana dina kaulinan, "meunang acquainted" saling jeung pamuter kahiji;
  • sanggeus narima bal, pamaén milih pamaén séjén milu jeung ngalirkeun bal ka manéhna. Jumlah total pass diitung;
  • unggal pamuter boga "énergi", nu ngurangan kalawan unggal lolos bal ku pamaén éta. Nalika énergi béak, pamaén ngaleungitkeun tina kaulinan, ngumumkeun pangsiun maranéhna;
  • lamun pamaén ditinggalkeun nyalira, anjeunna geuwat dibewarakeun miang na;
  • lamun kabeh pamaén dileungitkeun, pamaén kahiji nyatakeun tungtung game. Upami anjeunna ngantunkeun pertandingan sateuacana, maka éta tetep nuturkeun pertandingan pikeun ngarengsekeunana.

Pikeun ngabéréskeun masalah ieu, kuring bakal nganggo DSL kami pikeun prosés bisnis, anu ngamungkinkeun anjeun ngajelaskeun logika dina Kotlin sacara kompak, kalayan minimum boilerplate.

Dina aplikasi app1, prosés bisnis pamaén munggaran (anjeunna ogé anu ngamimitian kaulinan) bakal jalan:

kelas InitialPlayer

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

Salian ngalaksanakeun logika bisnis, kodeu di luhur tiasa ngahasilkeun modél obyék prosés bisnis anu tiasa ditingali salaku diagram. Kami henteu acan ngalaksanakeun visualizer, janten urang kedah nyéépkeun waktos ngagambar (di dieu kuring rada nyederhanakeun notasi BPMN ngeunaan panggunaan gerbang pikeun ningkatkeun konsistensi diagram sareng kode di luhur):

Integrasi gaya BPM

app2 bakal kalebet prosés bisnis pamuter sanés:

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

Diagram:

Integrasi gaya BPM

Dina aplikasi app3, kami bakal ngajantenkeun pamuter kalayan paripolah anu rada béda: tinimbang milih sacara acak pamuter salajengna, anjeunna bakal bertindak dumasar kana algoritma round-robin:

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

Upami teu kitu, kabiasaan pamuter urang teu béda ti saméméhna, jadi diagram teu robah.

Ayeuna urang peryogi tés pikeun ngajalankeun éta sadayana. Kuring ngan ukur masihan kodeu tés sorangan, supados henteu ngaganggu tulisan sareng boilerplate (saleresna, kuring nganggo lingkungan uji anu diciptakeun sateuacana pikeun nguji integrasi prosés bisnis anu sanés):

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;
}

Jalankeun tés, tingali log:

kaluaran konsol

Взята блокировка ключа 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!

Sababaraha kacindekan penting tiasa ditarik tina sadayana ieu:

  • lamun parabot diperlukeun sadia, pamekar aplikasi bisa nyieun interaksi integrasi antara aplikasi tanpa megatkeun jauh ti logika bisnis;
  • pajeulitna (kompléks) tina tugas integrasi anu merlukeun kompetensi rékayasa bisa disumputkeun jero kerangka lamun eta mimitina diteundeun handap dina arsitektur kerangka. Kasusah tugas (kasusah) teu bisa disumputkeun, jadi solusi pikeun tugas hésé dina kode bakal kasampak sasuai;
  • nalika ngamekarkeun logika integrasi, perlu tumut kana akun ahirna konsistensi jeung kurangna linearizability parobahan kaayaan sadaya pamilon integrasi. Ieu maksakeun urang pikeun ngahesekeun logika supados teu peka kana tatanan kajadian luar. Dina conto urang, pamuter kapaksa ilubiung dina kaulinan sanggeus anjeunna announces kaluar tina kaulinan: pamaén séjén bakal neruskeun ngalirkeun bal ka anjeunna nepi ka informasi ngeunaan kaluar na ngahontal sarta diolah ku sakabeh pamilon. Logika ieu henteu nuturkeun aturan kaulinan sareng mangrupikeun solusi kompromi dina kerangka arsitéktur anu dipilih.

Salajengna, hayu urang ngobrol ngeunaan rupa-rupa subtleties solusi urang, compromises jeung titik lianna.

Sadaya pesen dina hiji antrian

Sadaya aplikasi terpadu tiasa dianggo sareng hiji beus integrasi, anu dipidangkeun salaku calo éksternal, hiji BPMQueue pikeun pesen sareng hiji topik BPMTopic pikeun sinyal (acara). Ngaliwatan sadaya pesen ngaliwatan antrian tunggal nyaeta kompromi sorangan. Dina tingkat logika bisnis, anjeun ayeuna tiasa ngenalkeun saloba jinis pesen anyar anu anjeun pikahoyong tanpa ngarobih struktur sistem. Ieu nyederhanakeun signifikan, tapi mawa resiko tangtu, nu, dina konteks tugas has urang, seemed urang teu jadi signifikan.

Integrasi gaya BPM

Sanajan kitu, aya hiji subtlety dieu: unggal aplikasi nyaring "na" pesen tina antrian di lawang, ku ngaran domain na. Ogé, domain bisa dieusian dina sinyal, lamun kudu ngawatesan "wengkuan" sinyal ka aplikasi tunggal. Ieu kedah ningkatkeun rubakpita beus, tapi logika bisnis ayeuna kudu beroperasi kalawan ngaran domain: wajib pikeun alamat pesen, desirable pikeun sinyal.

Mastikeun reliabilitas beus integrasi

Reliabiliti diwangun ku sababaraha hal:

  • Calo pesen anu dipilih mangrupikeun komponén kritis arsitéktur sareng hiji titik kagagalan: éta kedah toleran lepat. Anjeun kedah nganggo ukur palaksanaan anu diuji waktos kalayan dukungan anu saé sareng komunitas anu ageung;
  • perlu pikeun mastikeun kasadiaan tinggi tina calo pesen, nu eta kudu fisik dipisahkeun tina aplikasi terpadu (kasadiaan tinggi aplikasi kalawan logika bisnis dilarapkeun leuwih hese tur mahal pikeun nyadiakeun);
  • calo nu wajib nyadiakeun "sahenteuna sakali" jaminan pangiriman. Ieu sarat wajib pikeun operasi dipercaya tina beus integrasi. Henteu peryogi jaminan tingkat "persis sakali": prosés bisnis biasana henteu sénsitip kana kadatangan pesen atanapi acara anu diulang-ulang, sareng dina tugas-tugas khusus anu penting ieu, langkung gampang pikeun nambihan cek tambahan kana logika bisnis tibatan nganggo terus-terusan. rada "mahal" " jaminan;
  • ngirim pesen jeung sinyal kudu kalibet dina transaksi umum kalawan parobahan dina kaayaan prosés bisnis jeung data domain. Pilihan anu dipikaresep nyaéta ngagunakeun pola Kotak kaluar Transactional, tapi bakal merlukeun hiji méja tambahan dina database na relay a. Dina aplikasi JEE, ieu tiasa disederhanakeun ku ngagunakeun manajer JTA lokal, tapi sambungan ka calo anu dipilih kedah tiasa dianggo dina modeu. XA;
  • Panangan pesen sareng acara anu datang ogé kedah damel sareng transaksi ngarobih kaayaan prosés bisnis: upami transaksi sapertos kitu digulung deui, maka resi pesen ogé kedah dibatalkeun;
  • pesen nu teu bisa dikirimkeun alatan kasalahan kudu disimpen dina toko misah D.L.Q. (Antrian Surat Mati). Jang ngalampahkeun ieu, kami nyiptakeun platform microservice anu misah anu nyimpen pesen sapertos dina panyimpenanna, ngindeks aranjeunna ku atribut (pikeun ngagolongkeun gancang sareng milarian), sareng ngalaan API pikeun ningali, ngirim deui ka alamat tujuan, sareng ngahapus pesen. Administrator sistem tiasa damel sareng jasa ieu ngalangkungan antarmuka wéb;
  • dina setélan calo, anjeun kedah nyaluyukeun jumlah pangiriman sareng telat antara pangiriman pikeun ngirangan kamungkinan pesen asup kana DLQ (éta ampir teu mungkin pikeun ngitung parameter optimal, tapi anjeun tiasa ngalakukeun sacara émpiris sareng nyaluyukeunana salami operasi);
  • toko DLQ kudu terus diawaskeun, sarta sistem ngawaskeun kudu ngabéjaan administrator sistem ambéh maranéhanana bisa ngabales gancang-gancang lamun pesen undelivered lumangsung. Ieu bakal ngurangan "zona ruksakna" gagalna atawa kasalahan logika bisnis;
  • beus integrasi kedah merhatikeun henteuna samentawis aplikasi: langganan topik kedah awét, sareng nami domain aplikasi kedah unik supados batur henteu nyobian ngolah pesen na tina antrian salami henteuna aplikasi.

Mastikeun kasalametan thread logika bisnis

Conto anu sami tina prosés bisnis tiasa nampi sababaraha pesen sareng acara sakaligus, pamrosésan anu bakal ngamimitian paralel. Dina waktos anu sami, pikeun pamekar aplikasi, sadayana kedah saderhana sareng aman.

Logika bisnis prosés ngolah unggal kajadian éksternal anu mangaruhan prosés bisnis ieu masing-masing. Acara ieu tiasa:

  • ngaluncurkeun conto prosés bisnis;
  • aksi pamaké nu patali jeung kagiatan dina prosés bisnis;
  • nampi pesen atanapi sinyal dimana conto prosés bisnis dilanggan;
  • béakna waktos diatur ku conto prosés bisnis;
  • tindakan kontrol via API (misalna prosés abort).

Masing-masing acara sapertos kitu tiasa ngarobih kaayaan conto prosés bisnis: sababaraha kagiatan tiasa ditungtungan sareng anu sanésna ngamimitian, nilai-nilai sipat pengkuh tiasa robih. Nutup kagiatan naon waé tiasa nyababkeun aktivasina hiji atanapi langkung kagiatan di handap ieu. Jalma, kahareupna bisa ngeureunkeun ngantosan acara séjén, atawa, lamun maranéhna teu butuh data tambahan, aranjeunna tiasa ngalengkepan dina urus sarua. Sateuacan nutup transaksi, kaayaan anyar tina prosés bisnis disimpen dina database, dimana eta bakal ngadagoan acara éksternal salajengna.

Data prosés bisnis pengkuh disimpen dina database relational mangrupakeun titik sinkronisasi processing pohara merenah lamun maké SELECT FOR UPDATE. Lamun hiji urus junun meunangkeun kaayaan prosés bisnis ti database pikeun ngarobah éta, mangka euweuh urus sejenna dina paralel bakal bisa meunang kaayaan anu sarua pikeun robah sejen, sarta sanggeus parantosan tina transaksi kahiji, kadua nyaéta dijamin narima kaayaan geus robah.

Ngagunakeun konci pesimis di sisi DBMS, urang minuhan sagala sarat diperlukeun Asid, sarta ogé nahan kamampuhan pikeun skala aplikasi kalawan logika bisnis ku cara ningkatkeun jumlah instansi ngajalankeun.

Sanajan kitu, konci pesimis ngancem urang kalawan deadlocks, nu hartina PILIH Pikeun Update kedah tetep dugi ka sababaraha timeout lumrah bisi deadlocks dina sababaraha kasus egregious dina logika bisnis.

Masalah sanésna nyaéta sinkronisasi ngamimitian prosés bisnis. Sanaos teu aya conto prosés bisnis, teu aya kaayaan dina pangkalan data ogé, ku kituna metode anu dijelaskeun moal jalan. Upami anjeun hoyong mastikeun keunikan conto prosés bisnis dina wengkuan khusus, maka anjeun peryogi sababaraha jinis obyék singkronisasi anu aya hubunganana sareng kelas prosés sareng lingkup anu saluyu. Pikeun ngabéréskeun masalah ieu, kami nganggo mékanisme ngonci anu béda anu ngamungkinkeun urang nyandak konci dina sumber daya sawenang anu ditunjuk ku konci dina format URI ngalangkungan jasa éksternal.

Dina conto urang, prosés bisnis InitialPlayer ngandung deklarasi

uniqueConstraint = UniqueConstraints.singleton

Ku alatan éta, log ngandung talatah ngeunaan nyokot jeung ngaleupaskeun konci konci pakait. Henteu aya pesen sapertos kitu pikeun prosés bisnis anu sanés: uniqueConstraint henteu disetel.

Masalah prosés bisnis sareng kaayaan pengkuh

Kadang-kadang gaduh kaayaan pengkuh henteu ngan ukur ngabantosan, tapi ogé ngahambat pangwangunan.
Masalah dimimitian nalika anjeun kedah parobihan kana logika bisnis sareng / atanapi modél prosés bisnis. Henteu aya parobahan sapertos kitu anu cocog sareng kaayaan prosés bisnis anu lami. Upami aya seueur instansi "hirup" dina pangkalan data, teras parobihan anu teu cocog tiasa nyababkeun seueur masalah, anu sering urang tepang nalika nganggo jBPM.

Gumantung kana jerona parobihan, anjeun tiasa ngalaksanakeun dua cara:

  1. nyieun tipe prosés bisnis anyar ku kituna teu nyieun parobahan sauyunan jeung nu heubeul, sarta ngagunakeun eta gaganti nu heubeul nalika ngamimitian instansi anyar. instansi heubeul bakal neruskeun dianggo "cara heubeul";
  2. migrasi kaayaan pengkuh tina prosés bisnis nalika ngamutahirkeun logika bisnis.

Cara kahiji leuwih basajan, tapi boga watesan jeung kalemahan, contona:

  • duplikasi logika bisnis dina loba model prosés bisnis, paningkatan dina volume logika bisnis;
  • mindeng diperlukeun transisi instan ka logika bisnis anyar (ampir salawasna dina watesan tugas integrasi);
  • pamekar teu nyaho di naon titik kasebut nyaéta dimungkinkeun pikeun mupus model leungit.

Dina prakték, urang ngagunakeun duanana pendekatan, tapi geus nyieun sababaraha kaputusan pikeun simplify kahirupan urang:

  • dina database, kaayaan pengkuh tina prosés bisnis disimpen dina formulir gampang dibaca tur gampang diolah: dina format string JSON. Ieu ngamungkinkeun anjeun pikeun ngalakukeun migrasi boh di jero aplikasi sareng di luar. Dina kasus ékstrim, Anjeun ogé bisa tweak eta kalawan handles (utamana mangpaat dina ngembangkeun salila debugging);
  • logika bisnis integrasi teu make ngaran prosés bisnis, ku kituna iraha wae kasebut nyaéta dimungkinkeun pikeun ngaganti palaksanaan salah sahiji prosés milu ku nu anyar, kalawan ngaran anyar (Contona, "InitialPlayerV2"). Beungkeutan lumangsung ngaliwatan ngaran pesen jeung sinyal;
  • model prosés boga angka versi, nu urang increment lamun urang nyieun parobahan sauyunan jeung model ieu, jeung nomer ieu disimpen sapanjang kalawan kaayaan conto prosés;
  • kaayaan pengkuh tina prosés dibaca ti dasarna munggaran kana model objék merenah nu prosedur migrasi tiasa dianggo kalayan lamun nomer versi model geus robah;
  • prosedur migrasi disimpen gigireun logika bisnis jeung disebut "puguh" pikeun tiap conto tina prosés bisnis dina waktu restorasi na tina database;
  • lamun perlu migrasi kaayaan sadaya instansi prosés gancang tur synchronously, solusi migrasi database leuwih klasik dipaké, tapi anjeun kudu digawekeun ku JSON dinya.

Naha kuring peryogi kerangka sanés pikeun prosés bisnis?

Solusi anu dijelaskeun dina tulisan ngamungkinkeun urang sacara signifikan nyederhanakeun kahirupan urang, ngalegaan sajumlah masalah anu direngsekeun dina tingkat pangembangan aplikasi, sareng ngajantenkeun ide pikeun misahkeun logika bisnis kana jasa mikro langkung pikaresepeun. Kanggo ieu, seueur padamelan anu parantos dilakukeun, kerangka "ringan" pisan pikeun prosés bisnis parantos didamel, ogé komponén jasa pikeun ngarengsekeun masalah anu diidentifikasi dina konteks rupa-rupa tugas anu diterapkeun. Simkuring boga kahayang pikeun babagi hasil ieu, pikeun mawa ngembangkeun komponén umum kana aksés kabuka dina lisénsi bébas. Ieu bakal merlukeun sababaraha usaha jeung waktu. Ngartos paménta pikeun solusi sapertos kitu tiasa janten insentif tambahan pikeun urang. Dina artikel anu diusulkeun, sakedik pisan perhatian dibayar ka kamampuan kerangka sorangan, tapi sababaraha di antarana katingali tina conto anu disayogikeun. Upami urang tetep nyebarkeun kerangka urang, tulisan anu misah bakal dikhususkeun pikeun éta. Samentawis waktos, kami bakal nganuhunkeun upami anjeun masihan sakedik tanggapan ku ngawalon patarosan:

Ngan pamaké nu kadaptar bisa ilubiung dina survey. Daptar, Punten.

Naha kuring peryogi kerangka sanés pikeun prosés bisnis?

  • 18,8%Sumuhun, abdi parantos lami milarian anu sapertos kieu.

  • 12,5%éta metot pikeun neuleuman leuwih ngeunaan palaksanaan anjeun, bisa jadi mangpaat2

  • 6,2%urang make salah sahiji frameworks aya, tapi urang mikir ngeunaan ngaganti eta1

  • 18,8%kami nganggo salah sahiji kerangka anu aya, sadayana cocog3

  • 18,8%coping tanpa kerangka3

  • 25,0%nulis sorangan4

16 pamaké milih. 7 pamaké abstained.

sumber: www.habr.com

Tambahkeun komentar