# 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`.

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

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

```scala
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` с подстановкой текущей сессии.

```scala
package ru.bitec.app.btk.templateObjectAttr

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

object Btk_AttrTemplateByClassAttrsApiMpi extends ApiMpiFactory[Btk_AttrTemplateByClassAttrsApiMpi]
```

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

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

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

```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`, чтобы класс стал реализацией этого контракта.

```scala
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` как обычный сессионный класс.

```scala
Btk_AttrTemplateByClassAttrsApiMpi().getsCaption(123.nl)
```

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

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

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

- `AutoNumDpiBuilder` — `ru.bitec.app.btk.Btk_Autonum_PkgMpi`;
- `DpiBuilder` — `ru.bitec.app.btk.Btk_DpiInjectionPkgMpi`;
- `DviBuilder` — `ru.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`:

```scala
...

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`:

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