MPI#

MPI (Module Programming Interface) — контракт, через который пакет сборки core обращается к бизнес-логике из пакета сборки src без прямой зависимости от реализации API.

MPI нужен, когда внутренняя логика из core должна использовать методы, реализованные в src. Для этого общая часть выносится в интерфейс MPI, а реализация остаётся в основном API-классе. Код из core работает с MPI-контрактом, а не с конкретной реализацией API.

MPI используется только в модулях gtk, btk и доменной логике.

Реализация MPI#

1. Определите интерфейс MPI

Создайте трейт, расширяющий ApiMpi, в пакете сборки core, но в том же пакете (package), что и ваш API.

Примечание

В зависимости от типа интерфейса используйте AviMpi, LibMpi или PkgMpi.

Пример для Btk_AttrTemplateByClassAttrsApi, который находится в пакете ru.bitec.app.btk.templateObjectAttr.

package ru.bitec.app.btk.templateObjectAttr

import ru.bitec.app.btk.eclipse.mpi.ApiMpi
import ru.bitec.app.gtk.lang.*

trait Btk_AttrTemplateByClassAttrsApiMpi extends ApiMpi {
  def idpOTGroup: NLong

  def getidAttribute(idp: NLong): NLong

  def getsCaption(idp: NLong): NString
}

2. Создайте фабрику MPI

Фабрика позволяет получать экземпляр MPI с подстановкой текущей сессии.

package ru.bitec.app.btk.templateObjectAttr

import ru.bitec.app.gtk.eclipse.mpi.ApiMpiFactory

object Btk_AttrTemplateByClassAttrsApiMpi extends ApiMpiFactory[Btk_AttrTemplateByClassAttrsApiMpi]

Имя объекта по соглашению должно совпадать с именем трейта и суффиксом Mpi. Фабрика наследуется от ApiMpiFactory с типом созданного трейта.

Примечание

В зависимости от типа интерфейса используйте AviMpiFactory, LibMpiFactory или PkgMpiFactory.

Итоговый файл Btk_AttrTemplateByClassAttrsApiMpi.scala:

package ru.bitec.app.btk.templateObjectAttr

import ru.bitec.app.gtk.eclipse.mpi.ApiMpiFactory
import ru.bitec.app.btk.eclipse.mpi.ApiMpi
import ru.bitec.app.gtk.lang.*
import ru.bitec.app.gtk.Lang.*

trait Btk_AttrTemplateByClassAttrsApiMpi extends ApiMpi {
  def idpOTGroup: NLong

  def getidAttribute(idp: NLong): NLong

  def getsCaption(idp: NLong): NString
}

object Btk_AttrTemplateByClassAttrsApiMpi extends ApiMpiFactory[Btk_AttrTemplateByClassAttrsApiMpi]

3. Реализуйте MPI в основном API-классе

Добавьте созданный интерфейс MPI к основному API-классу через with, чтобы класс стал реализацией этого контракта.

package ru.bitec.app.btk.templateObjectAttr

class Btk_AttrTemplateByClassAttrsApi extends <...>Dpi[<...>] with Btk_AttrTemplateByClassAttrsApiMpi {
  lazy val idpOTGroup: NLong = Btk_ObjectTypeApi().findByMnemoCodeAndClass(spGroupOTName, idClass)

  def getidAttribute(idp: NLong): NLong = {
    load(idp).get(_.idAttribute)
  }

  def getsCaption(idp: NLong): NString = {
    load(idp).get(_.sCaption)
  }
  
  // Остальной код класса
}

Использование MPI#

Используйте MPI как обычный сессионный класс.

Btk_AttrTemplateByClassAttrsApiMpi().getsCaption(123.nl)

Ограничения и особенности#

В построителях из пакета ru/bitec/app/gtk/meta/builders нельзя использовать контракты или прямое обращение к классу. Обращение должно выполняться только через MPI.

Для построителей уже созданы классы расширения с MPI:

  • AutoNumDpiBuilderru.bitec.app.btk.Btk_Autonum_PkgMpi;

  • DpiBuilderru.bitec.app.btk.Btk_DpiInjectionPkgMpi;

  • DviBuilderru.bitec.app.btk.Btk_DviInjectionLibMpi.

В этих классах реализуется логика для DVI, DPI и автонумерации.

В пакетах построителей используются два варианта реализации:

  • ru/bitec/app/gtk/meta/builders — новый код, в котором используется MPI. Используется в коробочном решении;

  • ru/bitec/app/gtk/meta/builders/old — старый код без MPI. Используется в проектных модулях.

Частые проблемы и их решения#

case class недоступен из core#

Если case class находится в объекте-компаньоне класса, для которого реализуется MPI, его необходимо вынести в фабрику MPI.

Если case class находится в пакете сборки src, его необходимо перенести в пакет сборки core.

Пример ru.bitec.app.btk.valueset.Btk_ValueSetApiMpi:

...

object Btk_ValueSetApiMpi extends ApiMpiFactory[Btk_ValueSetApiMpi] {
  case class ValueSet(private val idQueryOpt: Try[String], private val gidQueryOpt: Try[String]) {
    def idQuery: String = idQueryOpt.get

    def gidQuery: String = gidQueryOpt.get
  }
}

Поле объекта-компаньона недоступно из core#

Если в параметрах функции используется поле из объекта-компаньона, поле необходимо перенести в фабрику MPI.

Пример ru.bitec.app.btk.Btk_DpiInjectionPkgMpi:

object Btk_DpiInjectionPkgMpi extends PkgMpiFactory[Btk_DpiInjectionPkgMpi] {
  lazy val nLongNoValue: NLong = NLong(-1)
}