Integrasi gaya BPM

Integrasi gaya BPM

Halo, Habr!

Perusahaan kita duwe spesialisasi ing pangembangan solusi piranti lunak kelas ERP, sing paling gedhe dikuwasani dening sistem transaksional kanthi logika bisnis lan aliran dokumen a la EDMS. Versi produk kita saiki adhedhasar teknologi JavaEE, nanging kita uga aktif nyoba layanan mikro. Salah sawijining wilayah sing paling bermasalah kanggo solusi kasebut yaiku integrasi macem-macem subsistem sing ana ing domain jejer. Masalah Integrasi tansah menehi kita sirah ageng, preduli saka gaya arsitektur, teknologi tumpukan lan frameworks kita nggunakake, nanging bubar wis kemajuan ing mecahaken masalah kuwi.

Ing artikel sing dakkandhakake, aku bakal ngomong babagan pengalaman lan riset arsitektur sing diduweni NPO Krista ing wilayah sing ditemtokake. Kita uga bakal ndeleng conto solusi sing gampang kanggo masalah integrasi saka sudut pandang pangembang aplikasi lan ngerteni apa sing didhelikake ing kesederhanaan iki.

Penafian

Solusi arsitektur lan teknis sing diterangake ing artikel kasebut diusulake dening aku adhedhasar pengalaman pribadi ing konteks tugas tartamtu. Solusi kasebut ora ngaku universal lan bisa uga ora optimal ing kahanan panggunaan liyane.

Apa hubungane BPM karo iku?

Kanggo njawab pitakonan iki, kita kudu nliti luwih jero babagan spesifik masalah sing ditrapake kanggo solusi kita. Bagean utama logika bisnis ing sistem transaksional khas kita yaiku nglebokake data menyang database liwat antarmuka pangguna, verifikasi manual lan otomatis data iki, nindakake liwat sawetara alur kerja, nerbitake menyang sistem / database / arsip analitis liyane, ngasilake laporan. . Mangkono, fungsi utama sistem kanggo pelanggan yaiku otomatisasi proses bisnis internal.

Kanggo penak, kita nggunakake istilah "dokumen" ing komunikasi minangka sawetara abstraksi sakumpulan data sing digabungake karo kunci umum sing bisa "disambungake" alur kerja tartamtu.
Nanging babagan logika integrasi? Sawise kabeh, tugas integrasi digawe dening arsitektur sistem, sing "dipotong" dadi bagean dudu panyuwunan pelanggan, nanging ing pengaruh faktor sing beda banget:

  • tundhuk marang hukum Conway;
  • minangka asil nggunakake maneh subsistem sing sadurunge dikembangake kanggo produk liyane;
  • ing discretion arsitek, adhedhasar syarat non-fungsi.

Ana godaan gedhe kanggo misahake logika integrasi saka logika bisnis alur kerja utama, supaya ora ngrusak logika bisnis karo artefak integrasi lan nylametake pangembang aplikasi saka perlu kanggo nyelidiki fitur lanskap arsitektur sistem. Pendekatan iki nduweni sawetara kaluwihan, nanging praktik nuduhake ora efektif:

  • ngrampungake masalah integrasi biasane bali menyang pilihan sing paling gampang ing wangun panggilan sinkron amarga titik ekspansi winates ing implementasine alur kerja utama (kekurangan integrasi sinkron dibahas ing ngisor iki);
  • artefak integrasi isih nembus logika bisnis inti nalika umpan balik saka subsistem liyane dibutuhake;
  • pangembang aplikasi nglirwakake integrasi lan bisa gampang ngilangi kanthi ngganti alur kerja;
  • sistem mandek dadi kabèh siji saka sudut pandang pangguna, "lapisan" antarane subsistem dadi katon, lan operasi pangguna keluwih katon, miwiti transfer data saka siji subsistem liyane.

Pendekatan liyane yaiku nimbang interaksi integrasi minangka bagean integral saka logika bisnis inti lan alur kerja. Kanggo nyegah kualifikasi pangembang aplikasi saka skyrocketing, nggawe interaksi integrasi anyar kudu gampang lan effortless, karo minimal kesempatan kanggo milih solusi. Iki luwih angel ditindakake tinimbang sing katon: alat kasebut kudu cukup kuat kanggo nyedhiyakake macem-macem pilihan sing dibutuhake kanggo pangguna, tanpa ngidini dheweke "nembak awake dhewe." Ana akeh pitakonan sing kudu dijawab dening insinyur ing konteks tugas integrasi, nanging pangembang aplikasi ora kudu dipikirake ing karya saben dina: wates transaksi, konsistensi, atomicity, keamanan, skala, distribusi beban lan sumber daya, rute, marshaling, distribusi lan ngoper konteks, etc.. Sampeyan perlu kanggo kurban pangembang aplikasi Cithakan solusi cukup prasaja kang jawaban kanggo kabeh pitakonan kuwi wis didhelikake. Cithakan iki kudu cukup aman: owah-owahan logika bisnis asring banget, sing nambah risiko ngenalake kesalahan, biaya kesalahan kudu tetep ing tingkat sing cukup murah.

Nanging apa hubungane BPM? Ana akeh opsi kanggo ngleksanakake alur kerja...
Pancen, implementasine proses bisnis liyane populer banget ing solusi kita - liwat definisi deklaratif saka diagram transisi negara lan sambungan panangan karo logika bisnis kanggo transisi. Ing kasus iki, negara sing nemtokake posisi saiki "dokumen" ing proses bisnis minangka atribut saka "dokumen" kasebut dhewe.

Integrasi gaya BPM
Iki minangka proses katon ing wiwitan proyek

Popularitas implementasi iki amarga kesederhanaan relatif lan kacepetan nggawe proses bisnis linier. Nanging, amarga sistem piranti lunak terus-terusan dadi luwih rumit, bagean otomatis saka proses bisnis tuwuh lan dadi luwih rumit. Ana perlu kanggo dekomposisi, nggunakake maneh bagean pangolahan, uga pangolahan cabang supaya saben cabang dileksanakake ing podo karo. Ing kahanan kaya mengkono, alat dadi ora trep, lan diagram transisi negara ilang isi informasi (interaksi integrasi ora dibayangke ing diagram ing kabeh).

Integrasi gaya BPM
Iki minangka proses sing katon sawise sawetara pengulangan klarifikasi syarat.

Cara metu saka kahanan iki yaiku integrasi mesin jBPM menyang sawetara produk kanthi proses bisnis sing paling rumit. Ing wektu sing cendhak, solusi iki sukses: bisa ditindakake proses bisnis sing rumit nalika njaga diagram sing cukup informatif lan relevan ing notasi. BPMN2.

Integrasi gaya BPM
Bagéyan cilik saka proses bisnis sing rumit

Ing jangka panjang, solusi kasebut ora cocog karo pangarepan: intensitas tenaga kerja sing dhuwur kanggo nggawe proses bisnis liwat alat visual ora ngidini entuk indikator produktivitas sing bisa ditampa, lan alat kasebut dhewe dadi salah sawijining pangembang sing paling ora disenengi. Ana uga keluhan babagan struktur internal mesin, sing nyebabake akeh "patch" lan "kruk".

Aspek positif utama nggunakake jBPM yaiku kesadaran babagan mupangat lan cilaka saka kahanan proses bisnis sing terus-terusan. Kita uga weruh kamungkinan nggunakake pendekatan proses kanggo ngleksanakake protokol integrasi Komplek antarane aplikasi beda nggunakake interaksi bedo liwat sinyal lan pesen. Anane negara sing terus-terusan nduweni peran penting ing babagan iki.

Adhedhasar ing ndhuwur, kita bisa nyimpulake: Pendekatan proses ing gaya BPM ngidini kita ngrampungake macem-macem tugas kanggo ngotomatisasi proses bisnis sing saya tambah rumit, kanthi harmonis pas karo aktivitas integrasi menyang proses kasebut lan njaga kemampuan kanggo nampilake proses sing ditindakake kanthi visual ing notasi sing cocog.

Kekurangan panggilan sinkron minangka pola integrasi

Integrasi sinkron nuduhake telpon pamblokiran sing paling gampang. Siji subsistem tumindak minangka sisih server lan mbukak API kanthi cara sing dibutuhake. Subsistem liyane tumindak minangka sisih klien lan ing wektu sing tepat nelpon lan ngenteni asil. Gumantung saka arsitektur sistem, sisih klien lan server bisa ditemokake ing aplikasi lan proses sing padha, utawa ing macem-macem. Ing kasus kaloro, sampeyan kudu ngetrapake sawetara implementasi RPC lan nyedhiyakake marshalling paramèter lan asil telpon.

Integrasi gaya BPM

Pola integrasi iki nduweni kekurangan sing cukup gedhe, nanging akeh banget digunakake ing praktik amarga kesederhanaan. Kacepetan implementasine bisa nggumunake lan meksa sampeyan nggunakake maneh lan maneh nalika ngadhepi tenggat wektu, ngrekam solusi kasebut minangka utang teknis. Nanging uga kedadeyan yen pangembang sing ora duwe pengalaman nggunakake kanthi ora sadar, mung ora ngerti akibat negatif.

Saliyane kenaikan paling jelas ing konektivitas subsistem, ana uga masalah sing kurang jelas karo transaksi "tuwuh" lan "mulet". Pancen, yen logika bisnis nggawe sawetara owah-owahan, banjur transaksi ora bisa nyingkiri, lan transaksi, banjur, mblokir sumber daya aplikasi tartamtu sing kena pengaruh owah-owahan kasebut. Tegese, nganti siji subsistem ngenteni respon saka liyane, ora bakal bisa ngrampungake transaksi lan mbusak kunci. Iki kanthi signifikan nambah risiko macem-macem efek:

  • Responsiveness sistem ilang, pangguna ngenteni suwe kanggo jawaban kanggo panjalukan;
  • server umume mandheg nanggapi panjalukan pangguna amarga blumbang benang sing akeh banget: mayoritas benang dikunci ing sumber daya sing dikuwasani dening transaksi;
  • Deadlocks wiwit katon: kemungkinan kedadeyan kasebut gumantung banget marang durasi transaksi, jumlah logika bisnis lan kunci sing ana ing transaksi kasebut;
  • kesalahan wektu entek transaksi katon;
  • server "gagal" karo OutOfMemory yen tugas mbutuhake Processing lan ngganti jumlah gedhe saka data, lan ngarsane integrasi sinkron ndadekake angel banget pamisah Processing menyang transaksi "korek".

Saka sudut pandang arsitektur, panggunaan pamblokiran telpon sajrone integrasi ndadékaké mundhut kontrol kualitas subsistem individu: mokal kanggo mesthekake indikator kualitas target siji subsistem kanthi kapisah saka indikator kualitas subsistem liyane. Yen subsistem dikembangake dening tim sing beda-beda, iki masalah gedhe.

Prekara dadi luwih menarik yen subsistem sing diintegrasi ana ing aplikasi sing beda-beda lan sampeyan kudu nggawe owah-owahan sinkron ing loro-lorone. Kepiye carane njamin transaksionalitas owah-owahan kasebut?

Yen owah-owahan digawe ing transaksi kapisah, sampeyan kudu nyedhiyani pangecualian tanggung jawab lan ganti rugi, lan iki rampung ngilangi kauntungan utama integrasi sinkron - kesederhanaan.

Transaksi sing disebarake uga dipikirake, nanging ora digunakake ing solusi kita: angel kanggo njamin linuwih.

"Saga" minangka solusi kanggo masalah transaksi

Kanthi tuwuhing popularitas microservices, dikarepake kanggo Pola Saga.

Pola iki kanthi sampurna ngrampungake masalah transaksi dawa sing kasebut ing ndhuwur, lan uga ngembangake kemampuan ngatur kahanan sistem saka sisih logika bisnis: ganti rugi sawise transaksi gagal bisa uga ora mbalekake sistem kasebut menyang negara asline, nanging nyedhiyakake dalan pangolahan data alternatif. Iki uga ngidini sampeyan supaya ora mbaleni langkah-langkah pangolahan data sing wis rampung nalika nyoba nggawa proses kasebut menyang pungkasan sing "apik".

Apike, ing sistem monolitik, pola iki uga relevan nalika nerangake integrasi subsistem sing digandhengake lan efek negatif sing disebabake dening transaksi sing wis suwe lan kunci sumber daya sing cocog diamati.

Ing hubungan karo proses bisnis kita ing gaya BPM, ternyata gampang banget kanggo ngetrapake "Sagas": langkah-langkah individu saka "Saga" bisa ditemtokake minangka aktivitas ing proses bisnis, lan kahanan proses bisnis sing terus-terusan uga. nemtokake negara internal "Saga". Tegese, kita ora mbutuhake mekanisme koordinasi tambahan. Kabeh sing perlu iku makelar pesen sing ndhukung "paling ora sapisan" njamin minangka transportasi.

Nanging solusi iki uga duwe "rega" dhewe:

  • logika bisnis dadi luwih rumit: ganti rugi kudu digarap;
  • kudu ninggalake konsistensi lengkap, sing bisa dadi sensitif banget kanggo sistem monolitik;
  • Arsitektur dadi sethitik liyane rumit, lan perlu tambahan kanggo makelar pesen katon;
  • alat ngawasi lan administrasi tambahan bakal dibutuhake (sanajan ing umum iki apik: kualitas layanan sistem bakal nambah).

Kanggo sistem monolitik, sabdhoning nggunakake "Sag" ora pati jelas. Kanggo microservices lan SOA liyane, sing paling kamungkinan wis ana broker, lan konsistensi lengkap dikorbanake ing wiwitan proyek, keuntungan saka nggunakake pola iki bisa Ngartekno ngluwihi cacat, utamané yen ana API trep ing logika bisnis. tingkat.

Encapsulating logika bisnis ing microservices

Nalika kita miwiti eksperimen karo microservices, ana pitakonan sing cukup: ngendi kanggo nyelehake logika bisnis domain sing ana hubungane karo layanan sing njamin terus-terusan data domain?

Nalika ndeleng arsitektur saka macem-macem BPMSs, bisa uga katon cukup kanggo misahake logika bisnis saka persistence: nggawe lapisan platform lan domain-independen microservices sing mbentuk lingkungan lan wadhah kanggo eksekusi logika bisnis domain, lan desain terus-terusan data domain minangka. lapisan kapisah saka microservices banget prasaja lan entheng. Proses bisnis ing kasus iki nindakake orkestrasi layanan saka lapisan kegigihan.

Integrasi gaya BPM

Pendekatan iki nduweni kaluwihan gedhe banget: sampeyan bisa nambah fungsi platform kaya sing dikarepake, lan mung lapisan microservices platform sing cocog bakal dadi "lemak" saka iki. Proses bisnis saka domain apa wae langsung bisa nggunakake fungsi anyar platform kasebut sanalika dianyari.

Panaliten sing luwih rinci nuduhake kekurangan sing signifikan saka pendekatan iki:

  • layanan platform sing nglakokake logika bisnis saka akeh domain bebarengan nggawa risiko gedhe minangka titik siji saka Gagal. Owah-owahan sing kerep ing logika bisnis nambah risiko kesalahan sing nyebabake kegagalan sistem;
  • masalah kinerja: logika bisnis bisa digunakake karo data liwat antarmuka sing sempit lan alon:
    • data bakal sepisan maneh marshaled lan pompa liwat tumpukan jaringan;
    • layanan domain bakal kerep nyedhiyakake data luwih akeh tinimbang sing dibutuhake kanggo proses logika bisnis amarga ora cukup kemampuan kanggo paramèter panjaluk ing tingkat API eksternal layanan;
    • sawetara bêsik independen saka logika bisnis bisa bola-bali njaluk data sing padha kanggo Processing (masalah iki bisa mitigated dening nambah komponen sesi sing data cache, nanging iki luwih complicates arsitektur lan nggawe masalah relevansi data lan cache invalidation);
  • masalah transaksi:
    • proses bisnis kanthi negara terus-terusan, sing disimpen dening layanan platform, ora konsisten karo data domain, lan ora ana cara sing gampang kanggo ngatasi masalah iki;
    • nempatake pamblokiran data domain ing njaba transaksi: yen logika bisnis domain kudu nggawe owah-owahan sawise pisanan mriksa bener saka data saiki, iku perlu kanggo ngilangi kamungkinan saka owah-owahan competitive ing data diproses. Pamblokiran data eksternal bisa mbantu ngatasi masalah kasebut, nanging solusi kasebut nggawa risiko tambahan lan nyuda linuwih sakabèhé sistem;
  • kangelan tambahan nalika nganyari: ing sawetara kasus, layanan terus-terusan lan logika bisnis kudu dianyari synchronously utawa ing urutan ketat.

Pungkasane, kita kudu bali menyang dhasar: encapsulate data domain lan logika bisnis domain dadi siji layanan mikro. Pendekatan iki nyederhanakake persepsi layanan mikro minangka komponen integral saka sistem lan ora nyebabake masalah ing ndhuwur. Iki uga ora diwenehake kanthi gratis:

  • Standarisasi API dibutuhake kanggo interaksi karo logika bisnis (utamane, kanggo nyedhiyakake aktivitas pangguna minangka bagéan saka proses bisnis) lan layanan platform API; mbutuhake manungsa waé luwih ati-ati kanggo owah-owahan API, kompatibilitas maju lan mundur;
  • iku perlu kanggo nambah perpustakaan runtime tambahan kanggo mesthekake fungsi logika bisnis minangka bagéan saka saben microservice kuwi, lan iki menehi munggah kanggo syarat anyar kanggo perpustakaan kuwi: entheng lan minimal dependensi transitive;
  • pangembang logika bisnis kudu ngawasi versi perpustakaan: yen microservice durung rampung kanggo dangu, iku bakal paling kamungkinan ngemot versi outdated saka perpustakaan. Iki bisa dadi alangan sing ora dikarepke kanggo nambah fitur anyar lan mbutuhake migrasi logika bisnis lawas layanan kasebut menyang versi perpustakaan anyar yen ana owah-owahan sing ora kompatibel ing antarane versi.

Integrasi gaya BPM

Lapisan layanan platform uga ana ing arsitektur kasebut, nanging lapisan iki ora dadi wadah kanggo nglakokake logika bisnis domain, nanging mung lingkungane, nyedhiyakake fungsi "platform" tambahan. Lapisan kasebut dibutuhake ora mung kanggo njaga sifat entheng saka layanan mikro domain, nanging uga kanggo ngatur pusat.

Contone, aktivitas pangguna ing proses bisnis ngasilake tugas. Nanging, nalika nggarap tugas, pangguna kudu ndeleng tugas saka kabeh domain ing dhaptar umum, sing tegese kudu ana layanan registrasi tugas platform sing cocog, dibusak saka logika bisnis domain. Njaga enkapsulasi logika bisnis ing konteks kasebut cukup masalah, lan iki minangka kompromi liyane saka arsitektur iki.

Integrasi proses bisnis liwat mata pangembang aplikasi

Kaya sing kasebut ing ndhuwur, pangembang aplikasi kudu diabstraksi saka fitur teknis lan teknik kanggo ngetrapake interaksi sawetara aplikasi supaya bisa ngetung produktivitas pangembangan sing apik.

Ayo nyoba ngatasi masalah integrasi sing rada angel, sing diciptakake khusus kanggo artikel kasebut. Iki bakal dadi "game" tugas nglibatno telung aplikasi, ngendi saben wong nemtokake jeneng domain tartamtu: "app1", "app2", "app3".

Ing saben aplikasi, proses bisnis diluncurake sing wiwit "muter bal" liwat bus integrasi. Pesen kanthi jeneng "Ball" bakal tumindak minangka bal.

Aturan game:

  • pemain pisanan iku inisiator. Panjenenganipun ngajak pemain liyane kanggo game, miwiti game lan bisa mungkasi ing sembarang wektu;
  • pemain liyane ngumumake partisipasi ing game, "ngerti" saben liyane lan pemain pisanan;
  • sawise nampa werni, pamuter milih pemain liyane peserta lan liwat werni kanggo wong. Jumlah total transmisi diitung;
  • Saben pemain wis "energi" kang sudo karo saben pass saka werni dening pemain sing. Nalika energi entek, pemain ninggalake game, ngumumake mundur;
  • yen pamuter kiwa piyambak, langsung mbewarakke departure;
  • Nalika kabeh pemain wis ngilangi, pemain pisanan ngumumake game kasebut rampung. Yen dheweke ninggalake game luwih awal, dheweke tetep ngetutake game kanggo ngrampungake.

Kanggo ngatasi masalah iki, aku bakal nggunakake DSL kanggo proses bisnis, sing ngidini kita njlèntrèhaké logika ing Kotlin kanthi kompak, kanthi minimal boilerplate.

Proses bisnis pemain pisanan (alias inisiator game) bakal bisa digunakake ing aplikasi app1:

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

Saliyane nglakokake logika bisnis, kode ing ndhuwur bisa ngasilake model obyek saka proses bisnis, sing bisa digambarake ing wangun diagram. Kita durung ngleksanakake visualisasi, mula kita kudu nggambar sawetara wektu (ing kene aku rada nyederhanakake notasi BPMN babagan panggunaan gerbang kanggo nambah konsistensi diagram kanthi kode ing ngisor iki):

Integrasi gaya BPM

app2 bakal kalebu proses bisnis pemain liyane:

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

Ing aplikasi app3, kita bakal nggawe pemain kanthi prilaku sing rada beda: tinimbang milih pemain sabanjure kanthi acak, dheweke bakal tumindak miturut 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}")
}

Yen ora, prilaku pamuter ora beda-beda saka sadurunge, supaya diagram ora ngganti.

Saiki kita butuh tes kanggo mbukak kabeh iki. Aku mung menehi kode tes dhewe, supaya ora ngrusak artikel kanthi boilerplate (nyatane, aku nggunakake lingkungan tes sing digawe sadurunge kanggo nyoba integrasi proses bisnis liyane):

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

Ayo tes lan deleng log:

output 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!

Saka kabeh iki, kita bisa nggawe sawetara kesimpulan penting:

  • kanthi alat sing dibutuhake, pangembang aplikasi bisa nggawe interaksi integrasi antarane aplikasi tanpa ngganggu logika bisnis;
  • kerumitan tugas integrasi sing mbutuhake kompetensi engineering bisa didhelikake ing framework yen iki pisanan kalebu ing arsitektur framework. Kesulitan masalah ora bisa didhelikake, mula solusi kanggo masalah angel ing kode bakal katon kaya;
  • Nalika ngembangake logika integrasi, penting kanggo nganggep konsistensi pungkasan lan kekurangan linearizability saka owah-owahan ing negara kabeh peserta integrasi. Iki meksa kita nggawe rumit logika supaya ora sensitif marang urutan kedadeyan eksternal. Ing conto kita, pemain kepeksa melu game kasebut sawise ngumumake metu saka game kasebut: pemain liyane bakal terus ngirim bal menyang dheweke nganti informasi babagan metune tekan lan diproses dening kabeh peserta. Logika iki ora ngetutake aturan game lan minangka solusi kompromi ing kerangka arsitektur sing dipilih.

Sabanjure, kita bakal ngomong babagan macem-macem kerumitan solusi, kompromi lan poin liyane.

Kabeh pesen ana ing siji antrian

Kabeh aplikasi terpadu bisa karo siji bis integrasi, kang presented ing wangun maklar external, siji BPMQueue kanggo pesen lan siji topik BPMTopic kanggo sinyal (acara). Nempatake kabeh pesen liwat siji antrian iku dhewe kompromi. Ing tingkat logika bisnis, sampeyan saiki bisa ngenalake macem-macem jinis pesen anyar sing dikarepake tanpa nggawe owah-owahan ing struktur sistem. Iki minangka simplifikasi sing signifikan, nanging nggawa risiko tartamtu, sing ing konteks tugas khas kita ora katon penting banget kanggo kita.

Integrasi gaya BPM

Nanging, ana siji subtlety ing kene: saben aplikasi nyaring pesen "sawijining" saka antrian ing lawang, kanthi jeneng domain. Domain kasebut uga bisa ditemtokake ing sinyal yen sampeyan kudu mbatesi "ruang lingkup visibilitas" sinyal menyang siji aplikasi. Iki kudu nambah throughput bis, nanging logika bisnis saiki kudu operate karo jeneng domain: kanggo alamat pesen - wajib, kanggo sinyal - seng di pengeni.

Njamin Keandalan Bus Integrasi

Reliabilitas kasusun saka sawetara poin:

  • Broker pesen sing dipilih minangka komponen kritis arsitektur lan siji titik kegagalan: kudu cukup fault-tolerant. Sampeyan kudu nggunakake mung implementasine-dites wektu, karo support apik lan komunitas gedhe;
  • perlu kanggo mesthekake kasedhiyan dhuwur saka makelar pesen, sing kudu dipisahake sacara fisik saka aplikasi terpadu (kasedhiyan dhuwur aplikasi kanthi logika bisnis sing ditrapake luwih angel lan larang kanggo mesthekake);
  • broker kapekso kanggo nyedhiyani "paling sapisan" njamin pangiriman. Iki minangka syarat wajib kanggo operasi bis integrasi sing dipercaya. Ora perlu njamin level "persis sapisan": proses bisnis, minangka aturan, ora sensitif marang pesen utawa acara sing bola-bali, lan ing tugas khusus sing penting, luwih gampang nambah mriksa tambahan kanggo bisnis. logika saka kanggo terus-terusan nggunakake cukup "larang" " njamin;
  • ngirim pesen lan sinyal kudu melu ing transaksi sakabèhé karo owah-owahan ing negara pangolahan bisnis lan data domain. Pilihan sing luwih disenengi yaiku nggunakake pola Kothak metu Transaksional, nanging mbutuhake tabel tambahan ing database lan repeater. Ing aplikasi JEE, iki bisa disederhanakake kanthi nggunakake manajer JTA lokal, nanging sambungan menyang broker sing dipilih kudu bisa digunakake ing XA;
  • panangan pesen lan acara sing mlebu uga kudu nggarap transaksi sing ngganti kahanan proses bisnis: yen transaksi kasebut digulung maneh, mula panrimo pesen kasebut kudu dibatalake;
  • pesen sing ora bisa dikirim amarga kesalahan kudu disimpen ing panyimpenan kapisah D.L.Q. (Antrian Surat Mati). Kanggo tujuan iki, kita nggawe layanan mikro platform sing kapisah sing nyimpen pesen kasebut ing panyimpenan, ngindeks kanthi atribut (kanggo klompok lan telusuran cepet), lan mbukak API kanggo ndeleng, ngirim maneh menyang alamat tujuan, lan mbusak pesen. Administrator sistem bisa nggarap layanan iki liwat antarmuka web;
  • ing setelan broker, sampeyan kudu nyetel jumlah nyoba maneh pangiriman lan wektu tundha ing antarane pangiriman supaya bisa nyuda kemungkinan pesen mlebu DLQ (meh ora bisa ngetung paramèter optimal, nanging sampeyan bisa tumindak kanthi empiris lan nyetel nalika operasi. );
  • Toko DLQ kudu dipantau terus-terusan, lan sistem ngawasi kudu menehi tandha marang administrator sistem supaya nalika pesen sing ora dikirim, bisa nanggapi kanthi cepet. Iki bakal nyuda "wilayah sing kena pengaruh" saka kegagalan utawa kesalahan logika bisnis;
  • bis integrasi kudu ora sensitif marang ora ana aplikasi sementara: langganan topik kudu awet, lan jeneng domain aplikasi kudu unik supaya nalika aplikasi ora ana, wong liya ora bakal nyoba ngolah pesen saka antrian.

Njamin keamanan thread saka logika bisnis

Kayata proses bisnis sing padha bisa nampa sawetara pesen lan acara bebarengan, pangolahan sing bakal diwiwiti kanthi paralel. Ing wektu sing padha, kanggo pangembang aplikasi, kabeh kudu prasaja lan aman.

Logika bisnis proses ngolah saben acara eksternal sing mengaruhi proses bisnis kasebut kanthi individu. Acara kasebut bisa uga:

  • miwiti conto proses bisnis;
  • tumindak pangguna sing ana gandhengane karo kegiatan ing proses bisnis;
  • panrimo pesen utawa sinyal sing langganan conto proses bisnis;
  • pemicu wektu sing disetel dening conto proses bisnis;
  • tumindak kontrol liwat API (contone, gangguan proses).

Saben acara kasebut bisa ngganti kahanan proses bisnis: sawetara kegiatan bisa rampung lan liyane bisa diwiwiti, lan nilai properti sing terus-terusan bisa diganti. Nutup kegiatan apa wae bisa nyebabake aktifitas siji utawa luwih saka aktivitas ing ngisor iki. Sing, ing siji, bisa mungkasi nunggu acara liyane utawa, yen padha ora perlu data tambahan, bisa rampung ing transaksi padha. Sadurunge nutup transaksi kasebut, negara anyar proses bisnis disimpen ing database, ing ngendi bakal ngenteni kedadeyan eksternal sabanjure.

Data proses bisnis sing terus-terusan sing disimpen ing basis data relasional minangka titik sing trep banget kanggo nyinkronake pangolahan yen sampeyan nggunakake SELECT FOR UPDATE. Yen siji transaksi bisa entuk kahanan proses bisnis saka basis kanggo ngganti, mula ora ana transaksi liyane sing podo karo sing bakal bisa entuk kahanan sing padha kanggo owah-owahan liyane, lan sawise rampung transaksi pisanan, sing kapindho yaiku dijamin nampa negara wis diganti.

Nggunakake kunci pesimis ing sisih DBMS, kita ngrampungake kabeh syarat sing dibutuhake ACID, lan uga nahan kemampuan kanggo skala aplikasi kanthi logika bisnis kanthi nambah jumlah instan sing mlaku.

Nanging, kunci pesimis ngancem kita karo deadlocks, kang tegese PILIH UNTUK UPDATE isih kudu diwatesi kanggo sawetara wektu entek cukup yen deadlocks dumadi ing sawetara kasus egregious ing logika bisnis.

Masalah liyane yaiku sinkronisasi wiwitan proses bisnis. Nalika ora ana conto proses bisnis, ora ana negara ing basis data, mula cara sing diterangake ora bakal bisa digunakake. Yen sampeyan perlu kanggo mesthekake uniqueness saka conto proses bisnis ing orane katrangan tartamtu, sampeyan bakal mbutuhake sawetara jenis obyek sinkronisasi gadhah kelas proses lan orane katrangan cocog. Kanggo ngatasi masalah iki, kita nggunakake mekanisme ngunci beda sing ngidini kita njupuk kunci ing sumber sembarang sing ditemtokake dening tombol ing format URI liwat layanan external.

Ing conto kita, proses bisnis InitialPlayer ngemot deklarasi

uniqueConstraint = UniqueConstraints.singleton

Mulane, log ngemot pesen babagan njupuk lan ngeculake kunci kunci sing cocog. Ora ana pesen kasebut kanggo proses bisnis liyane: uniqueConstraint ora disetel.

Masalah proses bisnis kanthi negara sing terus-terusan

Kadhangkala duwe negara sing terus-terusan ora mung mbantu, nanging uga ngalang-alangi pembangunan.
Masalah diwiwiti nalika owah-owahan kudu ditindakake ing logika bisnis lan/utawa model proses bisnis. Ora saben owah-owahan kasebut cocog karo proses bisnis sing lawas. Yen ana akeh kedadeyan langsung ing database, banjur owah-owahan sing ora kompatibel bisa nyebabake akeh masalah, sing asring kita temoni nalika nggunakake jBPM.

Gumantung ing ambane owah-owahan, sampeyan bisa tumindak kanthi rong cara:

  1. nggawe jinis proses bisnis anyar supaya ora nggawe owah-owahan sing ora kompatibel karo sing lawas, lan gunakake tinimbang sing lawas nalika ngluncurake instansi anyar. Salinan lawas bakal terus digunakake "kaya sadurunge";
  2. migrasi kahanan proses bisnis sing terus-terusan nalika nganyari logika bisnis.

Cara pisanan luwih prasaja, nanging nduweni watesan lan kekurangan, contone:

  • duplikasi logika bisnis ing akeh model proses bisnis, nambah volume logika bisnis;
  • Asring transisi langsung menyang logika bisnis anyar dibutuhake (ing babagan tugas integrasi - meh tansah);
  • pangembang ora ngerti ing titik apa model outdated bisa dibusak.

Ing praktik kita nggunakake loro pendekatan, nanging wis nggawe sawetara keputusan kanggo nggawe urip luwih gampang:

  • Ing basis data, kahanan proses bisnis sing terus-terusan disimpen ing wangun sing gampang diwaca lan gampang diproses: ing string format JSON. Iki ngidini migrasi bisa ditindakake ing aplikasi lan eksternal. Minangka pilihan pungkasan, sampeyan bisa mbenerake kanthi manual (utamane migunani ing pangembangan nalika debugging);
  • logika bisnis integrasi ora nggunakake jeneng pangolahan bisnis, supaya ing sembarang wektu bisa ngganti implementasine saka salah siji pangolahan peserta karo anyar karo jeneng anyar (contone, "InitialPlayerV2"). Ikatan kasebut dumadi liwat jeneng pesen lan sinyal;
  • model proses wis nomer versi, kang kita nambah yen kita nggawe owahan kompatibel kanggo model iki, lan nomer iki disimpen bebarengan karo negara saka proses Kayata;
  • negara terus-terusan proses diwaca saka database pisanan menyang model obyek trep, kang prosedur migrasi bisa digunakake yen nomer versi model wis diganti;
  • prosedur migrasi diselehake ing jejere logika bisnis lan disebut "kesed" kanggo saben Kayata saka proses bisnis ing wektu pemugaran saka database;
  • yen sampeyan kudu migrasi kabeh kahanan proses kanthi cepet lan bebarengan, solusi migrasi database luwih klasik digunakake, nanging sampeyan kudu nggarap JSON.

Apa sampeyan butuh kerangka kerja liyane kanggo proses bisnis?

Solusi sing diterangake ing artikel kasebut ngidini kita nyederhanakake urip kanthi signifikan, nggedhekake macem-macem masalah sing ditanggulangi ing tingkat pangembangan aplikasi, lan nggawe ide kanggo misahake logika bisnis dadi layanan mikro sing luwih apik. Kanggo entuk iki, akeh karya sing ditindakake, kerangka kerja sing "ringan" banget kanggo proses bisnis digawe, uga komponen layanan kanggo ngatasi masalah sing diidentifikasi ing konteks saka macem-macem masalah aplikasi. Kita duwe kepinginan kanggo nuduhake asil kasebut lan nggawe pangembangan komponen umum mbukak akses ing lisensi gratis. Iki bakal mbutuhake sawetara gaweyan lan wektu. Ngerteni panjaluk solusi kasebut bisa dadi insentif tambahan kanggo kita. Ing artikel sing diusulake, sethithik banget diwenehi perhatian marang kemampuan kerangka kasebut, nanging sawetara sing katon saka conto sing ditampilake. Yen kita nerbitake kerangka kerja kita, artikel sing kapisah bakal dikhususake. Ing sawetoro wektu, kita bakal ngucapke matur nuwun yen sampeyan menehi saran sethithik kanthi mangsuli pitakon:

Mung pangguna pangguna sing bisa melu survey. mlebunggih.

Apa sampeyan butuh kerangka kerja liyane kanggo proses bisnis?

  • 18,8%Ya wis suwe aku nggoleki sing kaya ngene iki

  • 12,5%Aku kepengin sinau babagan implementasine sampeyan, bisa uga migunani2

  • 6,2%Kita nggunakake salah siji saka frameworks ana, nanging mikir bab ngganti1

  • 18,8%Kita nggunakake salah siji saka framework ana, kabeh iku nggoleki3

  • 18,8%kita ngatur tanpa kerangka3

  • 25,0%tulisen panjenengan 4

16 pangguna milih. 7 kedhaftar abstained.

Source: www.habr.com

Add a comment