Textus Samples 02:Component Packaging
Textus Samples は、Textus component の開発手順を、小さなサンプルを順に読み解きながら理解するためのサンプル集です。
本記事では、その中から 02-component 系を取り上げます。
01-minimal では、sample directory を --project-dev . で開発中 project として読み込み、最小 Operation を確認しました。
本記事では、その次の開発手順として、Component を CAR として package し、 component.d/*.car から明示的に読み込む形を確認します。
前提
本記事では、📄 Textus Samples: Launcherとインストール の手順で準備した環境を使います。
-
textus-tutorial-0.1.3を展開済み -
cozy/cncf/textuslauncher をインストール済み -
各 launcher の runtime を確認済み
-
01-minimalの最小 Operation を確認済み
download 手順は前提記事で扱っているため、ここでは再掲しません。
学ぶこと
-
source project 上の Component を packaged component artifact として扱う考え方
-
CAR が何を含み、runtime に何を渡すのか
-
component.d/*.carが packaged component の標準入力になること -
Operation の selector は、source から読んでも CAR から読んでも変わらないこと
-
car.dと class discovery は補助的な確認形であり、baseline はcomponent.d/*.carであること
目的と着目点
このサンプルは、開発中 classpath で動いた Component を、配布可能な packaged Textus component として cncf launcher / CNCF (Cloud Native Component Framework) engine に渡す手順を見るためのものです。
ここで重要なのは、Operation の意味を変えずに、runtime へ渡す境界だけを source project から CAR へ切り替えることです。
したがって、この記事の主題は run.sh の中身ではありません。学習対象は、Component source、packaged artifact、runtime surface の対応関係です。
概念の焦点
-
Component packaging は実装を runtime に渡す境界です。Operation の意味は変えず、配布形態だけを変えます。
-
CAR directory と class discovery は同じ結果を目指しますが、検証している責務が違います。
Componentソース
Component source は、 TestcompComponent で Component 名を定義し、 MainService で Service / Operation の surface を定義します。
package testcomp
import org.goldenport.Consequence
import org.goldenport.protocol.Request
import org.goldenport.protocol.operation.OperationResponse
import org.goldenport.protocol.spec.*
import org.goldenport.cncf.action.{ActionCall, QueryAction}
import org.goldenport.cncf.component.{Component, ComponentCreate, ComponentId}
import org.goldenport.schema.XString
final class TestcompComponent extends Component
object TestcompComponent extends Component.Factory {
val name = "testcomp"
val componentId = ComponentId(name)
protected def create_Component(params: ComponentCreate): Component =
TestcompComponent()
protected def create_Core(
params: ComponentCreate,
comp: Component
): Component.Core =
spec_create(
name,
componentId,
MainService
)
}
object MainService extends ServiceDefinition {
val specification = ServiceDefinition.Specification.Builder("main")
.operation(HelloOperation)
.build()
object HelloOperation extends OperationDefinition {
val specification = OperationDefinition.Specification.Builder("hello").copy(
response = ResponseDefinition(result = List(XString))
).build()
override def createOperationRequest(
req: Request
): Consequence[HelloQuery] =
Consequence.success(HelloQuery(req))
}
}
final case class HelloQuery(
request: Request
) extends QueryAction() {
override def createCall(core: ActionCall.Core): ActionCall =
HelloActionCall(core, this)
}
final case class HelloActionCall(
core: ActionCall.Core,
query: HelloQuery
) extends ActionCall {
override def execute(): Consequence[OperationResponse] =
response_string("Hello from testcomp")
}
このコードから読み取る点は、処理内容の複雑さではありません。Component 名、Service 名、Operation 名が runtime surface を作り、それが testcomp.main.hello という selector になる点です。
Packagingモデル
packaging では、source project から jar を作り、その jar と descriptor を CAR に入れます。CAR は Textus runtime に渡せる Component artifact です。
src/main/scala/testcomp/TestcompComponent.scala
|
v
target/scala-3.3.7/textus-samples-02-component_3-*.jar
|
v
component.d/testcomp.car
|
v
Textus runtime surface: testcomp.main.hello
この流れで重要なのは、source の構造と runtime から見える surface が対応していることです。package しても testcomp.main.hello という呼び出し方は変わりません。
CARの形
02-component の CAR は、次のような最小構造を持ちます。
testcomp.car
component-descriptor.yaml
component/
main.jar
component-descriptor.yaml は Component artifact の名前と Component 名を runtime に伝えます。 component/main.jar は実際の Component 実装です。
name: testcomp
version: 0.1.0
component: testcomp
この descriptor により、runtime は component.d/testcomp.car を単なる zip file ではなく、 testcomp Component artifact として扱えます。
読み込み形態
確認
確認では、長い packaging script を読むことよりも、packaged CAR が runtime surface として見えていることを確認します。
$ cd samples/02-component
$ bash run.sh
run.sh は、jar の作成、CAR の作成、 component.d/testcomp.car からの読み込み確認をまとめた batch form です。記事の学習対象は script の個々の行ではなく、出力に Component / Service / Operation が surface として現れることです。
結果の読み方
help 出力に testcomp Component、 main Service、 hello Operation が出れば、packaged artifact が runtime surface として認識されています。
operation を実行した場合、 testcomp.main.hello は Hello from testcomp を返します。ただし、このサンプルで中心に見るべきなのは戻り値そのものではなく、packaged CAR から同じ selector が提供されることです。
よくあるつまずき
-
古い jar が component directory に残っていると、修正した source ではなく前の artifact を読んでいることがあります。
-
開発 classpath と packaged artifact の結果を混同しないように、どの script がどちらを使うか確認します。
CNCFエンジン上の意味
Component は実行時に登録される単位であり、source からでも archive からでも同じ selector を提供します。
この分離により、開発時の編集速度と配布時の artifact 安定性を両立できます。
次へ
次の記事 📄 Textus Samples 03: CML生成Componentでは、CML (Cozy Modeling Language) を使った通常の Component authoring に進みます。
参照
用語集
- コンポーネント (Component)
-
責務・契約・依存関係を明示的に定義し、再利用可能で交換可能な単位としてカプセル化されたソフトウェア構成要素。論理モデルでは抽象構造単位として、物理モデルでは実装・デプロイメント単位として扱われる。
- Cloud Native Component Framework (CNCF)
-
Cloud Native Component Framework(CNCF)は、クラウド・アプリケーションを構成するコンポーネントを、単一かつ一貫した実行モデルで実行するためのフレームワークです。 Component / Service / Operation という構造を中核とし、command、server(REST / OpenAPI)、client、script といった異なる実行形態から、同一の Operation を再利用できることを特徴とします。 ログ、エラー処理、設定、配備といったクラウド・アプリケーションに必要な品質属性をフレームワーク側に集約することで、コンポーネントはドメイン・ロジックの実装に集中できます。 CNCF は、文芸モデル駆動開発および AI 支援開発を前提に、「何を実行するか」と「どのように呼び出すか」を分離するための実行基盤として設計されています。
- CML (Cozy Modeling Language)
-
CMLは、Cozyモデルを記述するための文芸モデル記述言語です。 SimpleModelingにおける分析モデルの中核を担うDSL(ドメイン固有言語)として設計されています。 モデル要素とその関係性を自然言語に近い文体で記述できるよう工夫されており、AIによる支援や自動生成との高い親和性を備えています。 CMLで記述された文芸モデルは、設計モデル、プログラムコード、技術文書などに変換可能な中間表現として機能します。