Textus Samples 02:Component Packaging

Created: 2026-07-06 Updated: 2026-07-13

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 / textus launcher をインストール済み

  • 各 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 は同じ結果を目指しますが、検証している責務 (Responsibility)が違います。

サンプルディレクトリ

samples/02-component
samples/02.a-car-dir-lab
samples/02.b-discover-classes-lab

サンプルの内容

02-component の中心は、 testcomp という Component です。Component は main Service を持ち、その中に hello Operation を持ちます。

つまり、runtime から見える selector は次の形になります。

testcomp.main.hello

この selector の意味は、 testcomp Component の main Service にある hello Operation を呼び出す、ということです。

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 として扱えます。

読み込み形態

02 系では、同じ Component を複数の読み込み形態で確認します。

  • 02-component : baseline です。packaged CAR を component.d/*.car から読み込みます。

  • 02.a-car-dir-lab : CAR を展開した car.d を見て、archive の中身と loader/debug の考え方を確認します。

  • 02.b-discover-classes-lab : packaged CAR ではなく、開発中 project の class から Component を見つける形を確認します。

この3つは別々の Component を学ぶためのものではありません。同じ Component を、source、package、inspection のどの境界で runtime に渡すかを比較するためのものです。

確認

確認では、長い 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 安定性を両立できます。

次へ

次の記事 cncf-samples-03-cml-component.doxでは、CML (Cozy Modeling Language) を使った通常の Component authoring に進みます。

参照

用語集

Textus

Textusは、SimpleModelingによって実現されたソフトウェアを動作させるための実行基盤です。アプリケーションに共通する実行、運用、連携の機構を提供します。

操作 (Operation)

UMLにおけるOperationは、関連するBehaviorを呼び出すための名前、型、Parameter、Constraintを定めるClassifierのBehavioral Featureです。Operationは呼び出し契約を定め、MethodなどのBehaviorがその実現を担います。

コンポーネント (Component)

責務・契約・依存関係を明示的に定義し、再利用可能で交換可能な単位としてカプセル化されたソフトウェア構成要素。論理モデルでは抽象構造単位として、物理モデルでは実装・デプロイメント単位として扱われる。

Cloud Native Component Framework (CNCF)

Cloud Native Component Framework(CNCF)は、クラウド・アプリケーションを構成するコンポーネントを、単一かつ一貫した実行モデルで実行するためのフレームワークです。 Component / Service / Operation という構造を中核とし、command、server(REST / OpenAPI)、client、script といった異なる実行形態から、同一の Operation を再利用できることを特徴とします。 ログ、エラー処理、設定、配備といったクラウド・アプリケーションに必要な品質属性をフレームワーク側に集約することで、コンポーネントはドメイン・ロジックの実装に集中できます。 CNCF は、文芸モデル駆動開発および AI 支援開発を前提に、「何を実行するか」と「どのように呼び出すか」を分離するための実行基盤として設計されています。

責務 (Responsibility)

Responsibilityとは、ObjectまたはRoleが、何を知り、判断し、行い、守るべきかを表す義務です。構造上の情報だけでなく、規則とBehaviorの所有を定めます。

Cozy Modeling Language (CML)

CML(Cozy Modeling Language)は、オブジェクト・モデルのうち、プログラム生成と実行へ接続する実行可能モデルを記述するSimpleModelingの形式モデリング言語です。