本文へスキップ

spine.App ​

Appは、コンストラクタ、HTTPルート、インターセプター、イベントコンシューマー、WebSocketハンドラー、ユーザー定義のトランスポートを登録し、アプリケーションを実行するためのエントリーポイントです。このリファレンスでは、Spine v0.5.1の公開APIと起動オプションを扱います。

go
import "github.com/NARUBROWN/spine"

インターフェース定義 ​

go
type App interface {
    // 생성자 선언
    Constructor(constructors ...any)
    // 라우트 선언
    Route(method string, path string, handler any, opts ...router.RouteOption)
    // 인터셉터 선언
    Interceptor(interceptors ...core.Interceptor)
    // HTTP/WebSocket 범위를 지정한 전역 인터셉터 선언
    InterceptorFor(scope boot.InterceptorScope, interceptors ...core.Interceptor)
    // HTTP 서버 확장 (Echo 등)
    Transport(fn func(any))
    // 독립 실행되는 사용자 정의 전송 방식 등록
    RegisterTransport(t core.CustomTransport)
    // 네트워크 연결 없이 설정 검증
    Validate(opts boot.Options) error
    // 실행
    Run(opts boot.Options) error
    // 이벤트 소비자 레지스트리 반환
    Consumers() *consumer.Registry
    // 웹소켓 레지스트리 반환
    WebSocket() *ws.Registry
}

Routeのオプションは、アプリのコードからpkg/routeの関数で渡します。戻り値の型が内部パッケージにあるConsumers()とWebSocket()も、型を直接宣言せずにメソッドを続けて呼び出せます。

コンストラクタ ​

New ​

go
func New() App

新しいSpineアプリケーションのインスタンスを作成します。

戻り値

  • App - アプリケーションのインスタンス

例

go
app := spine.New()
app.Run(boot.Options{
    Address: ":8080",
    HTTP: &boot.HTTPOptions{},
})

メソッド ​

Constructor ​

go
Constructor(constructors ...any)

依存性注入コンテナにコンストラクタ関数を登録します。コンストラクタの引数の型に応じて、登録済みのほかのオブジェクトを注入します。

引数

  • constructors - コンストラクタ関数(可変長引数)

コンストラクタの規則

  • 関数である必要があります
  • 戻り値は正確に1つである必要があります
  • 引数の型を提供するコンストラクタも登録する必要があります

例

go
// 의존성 없는 생성자
func NewUserRepository() *UserRepository {
    return &UserRepository{}
}

// 의존성 있는 생성자
func NewUserController(repo *UserRepository) *UserController {
    return &UserController{repo: repo}
}

// 이벤트 소비자 생성자
func NewOrderConsumer() *OrderConsumer {
    return &OrderConsumer{}
}

app.Constructor(
    NewUserRepository,
    NewUserController,
    NewOrderConsumer,
)

Route ​

go
Route(method string, path string, handler any, opts ...router.RouteOption)

HTTPルートを登録します。methodの前後の空白を取り除き、大文字に変換します。

引数

  • method - HTTPメソッド("GET"、"POST"、"PUT"、"DELETE"など)。大文字と小文字は区別しません
  • path - URLパスのパターン。:param形式でパスパラメータを定義します
  • handler - コントローラーのメソッド式
  • opts - ルートオプション(省略可能)

パスのパターン

  • /users - 静的パス
  • /users/:id - 単一のパラメータ
  • /users/:userId/posts/:postId - 複数のパラメータ

例

go
// 기본 라우트
app.Route("GET", "/users", (*UserController).List)
app.Route("GET", "/users/:id", (*UserController).GetUser)
app.Route("POST", "/users", (*UserController).CreateUser)
app.Route("PUT", "/users/:id", (*UserController).UpdateUser)
app.Route("DELETE", "/users/:id", (*UserController).DeleteUser)

// 중첩 경로
app.Route("GET", "/users/:userId/posts/:postId", (*PostController).GetPost)

ルートオプション ​

route.WithInterceptorsで、特定のルートだけにインターセプターを適用します。

go
import "github.com/NARUBROWN/spine/pkg/route"

// 라우트별 인터셉터 적용
app.Route(
    "GET",
    "/users/:id",
    (*UserController).GetUser,
    route.WithInterceptors(&LoggingInterceptor{}),
)

// 타입이 있는 nil 포인터는 컨테이너에서 조회
app.Route(
    "POST",
    "/admin/users",
    (*AdminController).CreateUser,
    route.WithInterceptors((*AuthInterceptor)(nil)),
)

// 여러 인터셉터 적용
app.Route(
    "POST",
    "/admin/users",
    (*AdminController).CreateUser,
    route.WithInterceptors(
        (*AuthInterceptor)(nil),
        &AdminRoleInterceptor{},
    ),
)

Interceptor ​

go
Interceptor(interceptors ...core.Interceptor)

HTTPリクエストとWebSocketメッセージにグローバルインターセプターを登録します。app.Interceptor(...)はapp.InterceptorFor(boot.InterceptorAll, ...)と同じです。登録順にPreHandleを実行し、逆順にPostHandle、BeforeResponse、AfterCompletionを実行します。

同じポインターのインスタンスを複数のスコープに登録すると、スコープをまとめて1回実行します。同じ型でも、異なるインスタンスはそれぞれ実行します。(*AuthInterceptor)(nil)のような型付きnilポインターを登録すると、コンテナから共有インスタンスを取得して使用します。

引数

  • interceptors: インターセプターのインスタンスの一覧

実行順序

  1. グローバルインターセプターのPreHandleを登録順に実行します。
  2. ルーティング後に入力値を解釈します。
  3. ルートインターセプターのPreHandleを登録順に実行します。
  4. コントローラーを実行してレスポンスを準備した後、実行後フックを呼び出します。
  5. ルートインターセプター、グローバルインターセプターの順にPostHandleを実行します。それぞれ登録の逆順です。
  6. 同じ逆順でBeforeResponseを実行した後、実際のレスポンスを書き込みます。
  7. レスポンス処理後、同じ逆順でAfterCompletionを実行します。

エラーや処理中断時の動作は、インターセプターリファレンスを参照してください。

例

go
app.Interceptor(
    cors.New(cors.Config{
        AllowOrigins: []string{"*"},
        AllowMethods: []string{"GET", "POST", "OPTIONS"},
        AllowHeaders: []string{"Content-Type"},
    }),
    &LoggingInterceptor{},
)

InterceptorFor ​

go
InterceptorFor(scope boot.InterceptorScope, interceptors ...core.Interceptor)

グローバルインターセプターを適用するトランスポートを選択します。イベントコンシューマーには適用しません。

スコープ適用対象
boot.InterceptorHTTPHTTPリクエスト
boot.InterceptorWebSocketWebSocketメッセージと、サポートされる接続前の処理
boot.InterceptorAll両方のスコープ
go
app.InterceptorFor(boot.InterceptorHTTP, corsInterceptor)
app.InterceptorFor(boot.InterceptorWebSocket, wsAuthInterceptor)
app.InterceptorFor(boot.InterceptorAll, loggingInterceptor)

Transport ​

go
Transport(fn func(any))

HTTPサーバーのEchoインスタンスを設定するコールバックを登録します。コールバックでミドルウェアや静的ファイルのパスを追加できます。

引数

  • fn - Echoインスタンスを受け取るコールバック関数

例

go
import "github.com/labstack/echo/v4"
import "github.com/labstack/echo/v4/middleware"

app.Transport(func(e any) {
    echo := e.(*echo.Echo)

    // Echo 미들웨어 추가
    echo.Use(middleware.RequestID())

    // 정적 파일 서빙
    echo.Static("/static", "public")
})

RegisterTransport ​

go
RegisterTransport(t core.CustomTransport)

HTTPパイプラインと独立して実行するトランスポートを登録します。たとえば、gRPCサーバーをSpineの依存性注入と起動および終了処理に接続するときに使用します。

引数

  • t - core.CustomTransportインターフェースの実装

CustomTransportインターフェース

go
// core/transport.go
type CustomTransport interface {
    // Init은 DI 컨테이너 준비 이후 호출됩니다.
    Init(container Container) error
    // Start는 Init 이후 별도 고루틴에서 호출됩니다.
    Start() error
    // Stop은 정상 종료 시 호출됩니다.
    Stop(ctx context.Context) error
}

type Container interface {
    Resolve(t reflect.Type) (any, error)
}

例

go
type GRPCTransport struct {
    server *grpc.Server
}

func (t *GRPCTransport) Init(container core.Container) error {
    // 컨테이너에서 서비스 조회
    svc, err := container.Resolve(reflect.TypeOf((*MyService)(nil)))
    if err != nil {
        return err
    }
    t.server = grpc.NewServer()
    RegisterMyServiceServer(t.server, svc.(*MyService))
    return nil
}

func (t *GRPCTransport) Start() error {
    lis, err := net.Listen("tcp", ":9090")
    if err != nil {
        return err
    }
    return t.server.Serve(lis)
}

func (t *GRPCTransport) Stop(ctx context.Context) error {
    t.server.GracefulStop()
    return nil
}

app.RegisterTransport(&GRPCTransport{})

Consumers ​

go
Consumers() *consumer.Registry

イベントコンシューマーを登録するためのレジストリを返します。KafkaやRabbitMQから受信するイベント名とハンドラーを登録します。コンシューマーのコンストラクタもConstructorで登録する必要があります。

戻り値

  • *consumer.Registry - コンシューマーレジストリ

例

go
// 이벤트 소비자 등록
app.Consumers().Register(
    "order.created",            // 토픽/이벤트 이름
    (*OrderConsumer).OnCreated, // 핸들러 메서드
)

app.Consumers().Register(
    "stock.created",
    (*StockConsumer).OnCreated,
)

イベントハンドラー ​

イベントハンドラーは、標準コンテキスト、イベント名、イベントDTOを引数として受け取れます。

go
type OrderConsumer struct{}

func NewOrderConsumer() *OrderConsumer {
    return &OrderConsumer{}
}

// 이벤트 핸들러
func (c *OrderConsumer) OnCreated(
    ctx context.Context,
    eventName string,
    event OrderCreated,
) error {
    log.Println("이벤트 수신:", eventName)
    log.Println("주문 ID:", event.OrderID)
    return nil
}

// 이벤트 DTO
type OrderCreated struct {
    OrderID int64     `json:"order_id"`
    At      time.Time `json:"at"`
}

WebSocket ​

go
WebSocket() *ws.Registry

WebSocketレジストリを返します。WebSocketのパスとハンドラーを登録します。

戻り値

  • *ws.Registry - WebSocketレジストリ

例

go
if err := app.WebSocket().Register("/ws/chat", (*ChatController).OnMessage); err != nil {
    log.Fatal(err)
}

WebSocketハンドラー ​

WebSocketハンドラーは、wsパッケージの接続IDとメッセージDTOを引数として受け取ります。次の例では接続ごとの送信オブジェクトを保存し、メッセージをすべての接続に送信します。ChatControllerのコンストラクタはConstructorで登録します。

go
import "github.com/NARUBROWN/spine/pkg/ws"

type ChatController struct {
    mu      sync.RWMutex
    clients map[string]ws.Sender
}

func NewChatController() *ChatController {
    return &ChatController{clients: make(map[string]ws.Sender)}
}

type ChatMessage struct {
    Message string `json:"message"`
}

type ChatEvent struct {
    Type    string `json:"type"`
    From    string `json:"from"`
    Message string `json:"message"`
    At      string `json:"at"`
}

func (c *ChatController) OnMessage(
    ctx context.Context,
    connID ws.ConnectionID,
    msg ChatMessage,
) error {
    sender, ok := ctx.Value(ws.SenderKey).(ws.Sender)
    if ok && sender != nil {
        c.mu.Lock()
        c.clients[connID.Value] = sender
        c.mu.Unlock()
    }

    message := strings.TrimSpace(msg.Message)
    if message == "" {
        return nil
    }

    payload, err := json.Marshal(ChatEvent{
        Type:    "message",
        From:    connID.Value,
        Message: message,
        At:      time.Now().UTC().Format(time.RFC3339),
    })
    if err != nil {
        return err
    }

    c.mu.RLock()
    clients := make(map[string]ws.Sender, len(c.clients))
    for id, client := range c.clients {
        clients[id] = client
    }
    c.mu.RUnlock()

    var firstErr error
    for id, client := range clients {
        if err := client.Send(ws.TextMessage, payload); err != nil {
            if firstErr == nil {
                firstErr = err
            }
            c.mu.Lock()
            delete(c.clients, id)
            c.mu.Unlock()
        }
    }
    return firstErr
}

WebSocketはHTTPサーバーと同じEchoインスタンスにパスを登録するため、boot.Options.HTTPが必要です。オリジン、接続数、認証の設定は、セキュリティ設定を参照してください。

Validate ​

go
Validate(opts boot.Options) error

ネットワーク接続を開かず、サーバーやコンシューマーを起動せずに起動オプションを検証します。KafkaとRabbitMQの接続設定、HTTPとWebSocketの制限値、グローバルインターセプターのnilの有無とスコープなどを確認します。Runも起動前に同じ検証を行います。

エラーがある場合は*boot.ConfigErrorを返します。Issuesの各項目には、設定の位置を示すPath、エラー分類を示すCode、説明のMessage、修正方法を示すHintがあります。有効なオプションの場合はnilです。

Validateはコンストラクタの依存関係を生成せず、ブローカーにも接続しません。コンストラクタとルートの適合性、DI構成、実際のインフラへの接続まで成功したとは判断できません。

go
options := boot.Options{Address: ":8080", HTTP: &boot.HTTPOptions{}}
if err := app.Validate(options); err != nil {
    log.Fatal(err)
}
if err := app.Run(options); err != nil {
    log.Fatal(err)
}

Run ​

go
Run(opts boot.Options) error

登録した構成とboot.Optionsに従い、HTTPサーバー、WebSocket、イベントコンシューマー、ユーザー定義のトランスポートを起動します。実行中は終了シグナルまたはランタイムエラーを待機します。設定、初期化、実行、終了に失敗すると、エラーを返します。

go
if err := app.Run(boot.Options{
    Address: ":8080",
    HTTP: &boot.HTTPOptions{},
}); err != nil {
    log.Fatal(err)
}

boot.Options ​

サーバーの実行と外部インフラの使用有無を指定します。HTTP、Kafka、RabbitMQがnilの場合、その機能を構成しません。

go
import "github.com/NARUBROWN/spine/pkg/boot"

構造体定義 ​

go
type Options struct {
    Address                string
    EnableGracefulShutdown bool
    ShutdownTimeout        time.Duration
    Kafka                  *KafkaOptions
    RabbitMQ               *RabbitMqOptions
    HTTP                   *HTTPOptions
}

type HTTPOptions struct {
    GlobalPrefix      string
    DisableRecover    bool
    ReadHeaderTimeout time.Duration
    ReadTimeout       time.Duration
    WriteTimeout      time.Duration
    IdleTimeout       time.Duration
    MaxHeaderBytes    int
    MaxBodyBytes      int64
    WebSocket         WebSocketOptions
}
Optionsフィールド意味とデフォルトの動作
AddressHTTPサーバーがリッスンするアドレス。例: ":8080"
EnableGracefulShutdowntrueの場合、HTTPサーバーでSIGINT、SIGTERMを受け取り、正常終了します。デフォルトはfalse
ShutdownTimeoutHTTPとユーザー定義のトランスポートの終了時に使用する待機時間。0の場合は10秒。負の値は指定できません
KafkaKafkaの発行と消費の設定。nilの場合は無効
RabbitMQRabbitMQの発行と消費の設定。nilの場合は無効
HTTPHTTPとWebSocketの設定。nilの場合はHTTPサーバーが無効
HTTPOptionsフィールド0または省略時制約
GlobalPrefixプレフィックスなし以下のパス規則を適用
DisableRecoverfalse。panicをHTTP 500に変換するリカバリーミドルウェアを使用trueの場合はミドルウェアを無効化
ReadHeaderTimeout5秒負の値は指定不可
ReadTimeout30秒負の値は指定不可
WriteTimeout30秒負の値は指定不可
IdleTimeout120秒負の値は指定不可
MaxHeaderBytes1 MiB負の値は指定不可
MaxBodyBytes32 MiB負の値の場合は本文サイズの制限を無効化
WebSocketWebSocketのデフォルト値boot.WebSocketOptions

GlobalPrefixの検証規則 ​

ValidateとRunは、次の規則に従わない値をHTTP_GLOBAL_PREFIX_INVALID設定エラーとして返します。空文字列の場合はプレフィックスを適用しません。

  • "/"で始まる必要があります
  • ":"(パスパラメータ)を含めることはできません
  • "*"(ワイルドカード)を含めることはできません
  • 末尾の"/"は自動で取り除かれます
go
// 유효한 접두사
HTTP: &boot.HTTPOptions{GlobalPrefix: "/api/v1"}
HTTP: &boot.HTTPOptions{GlobalPrefix: "/v2"}

// Validate와 Run이 설정 오류 반환
HTTP: &boot.HTTPOptions{GlobalPrefix: "api"}       // '/'로 시작하지 않음
HTTP: &boot.HTTPOptions{GlobalPrefix: "/api/:ver"}  // 경로 매개변수 포함
HTTP: &boot.HTTPOptions{GlobalPrefix: "/api/*"}     // 와일드카드 포함

基本的な使用方法 ​

go
app.Run(boot.Options{
    Address: ":8080",
    HTTP: &boot.HTTPOptions{},
})

正常終了 ​

go
app.Run(boot.Options{
    Address:                ":8080",
    EnableGracefulShutdown: true,
    ShutdownTimeout:        10 * time.Second,
    HTTP: &boot.HTTPOptions{
        GlobalPrefix: "/api/v1",
    },
})

HTTPの正常終了を有効にすると、SIGINT、SIGTERMを受け取って新しいリクエストの受け入れを停止し、処理中のリクエストを待機します。WebSocket接続を閉じ、イベントコンシューマーを停止した後、ユーザー定義のトランスポートのStop(ctx)を呼び出します。

ShutdownTimeoutは、HTTPとユーザー定義のトランスポートの終了コンテキストに適用します。コンシューマーのStopは、処理、ACK/NACK、リーダーが終了するまで待機するため、この値をプロセス全体の強制終了期限と解釈しないでください。HTTPを使用せず、コンシューマーやユーザー定義のトランスポートだけを実行する場合も終了シグナルを処理します。

Recoverミドルウェア ​

デフォルトでは、panicをHTTP 500レスポンスに変換するリカバリーミドルウェアが有効です。

go
// Recover 비활성화
HTTP: &boot.HTTPOptions{
    DisableRecover: true,
}

Kafka設定 ​

KafkaOptions ​

ReadとWriteは独立しています。いずれかを有効にするとBrokersが必要になり、Readを使用する場合はGroupIDも必要です。個別のトランスポート設定がない場合、TLS 1.2以上を適用します。

go
type KafkaOptions struct {
    // Kafka 브로커 주소 목록
    Brokers []string

    // 사용자 정의 TLS, 리더 Dialer, 라이터 Transport
    TLS *tls.Config
    Dialer *kafka.Dialer
    Transport *kafka.Transport

    // 격리된 로컬 환경의 평문 연결 명시적 허용
    AllowInsecureTransport bool

    // 리더 오류와 ACK/NACK 실패 뒤 재연결 정책
    ConsumerRetry ConsumerRetryOptions

    // 이벤트 소비 설정
    // nil이면 Kafka 소비자를 활성화하지 않음
    Read *KafkaReadOptions

    // 이벤트 발행 설정
    // nil이면 Kafka로 이벤트를 발행하지 않음
    Write *KafkaWriteOptions
}

type KafkaReadOptions struct {
    // Kafka 소비자 그룹 ID
    GroupID string
}

type KafkaWriteOptions struct {
    // 이벤트 이름 앞에 붙일 토픽 접두사
    TopicPrefix string
}

例 ​

go
app.Run(boot.Options{
    Address: ":8080",
    Kafka: &boot.KafkaOptions{
        Brokers: []string{"localhost:9092"},
        AllowInsecureTransport: true, // 격리된 로컬 개발 환경 전용
        Read: &boot.KafkaReadOptions{
            GroupID: "my-consumer-group",
        },
        Write: &boot.KafkaWriteOptions{
            TopicPrefix: "myapp.",
        },
    },
    HTTP: &boot.HTTPOptions{},
})

RabbitMQ設定 ​

RabbitMqOptions ​

ReadとWriteは独立しています。有効にした役割ごとにExchangeを指定します。デフォルトではamqps:// URLが必須です。平文のamqp://は、隔離された開発環境だけで明示的に許可します。

go
type RabbitMqOptions struct {
    // RabbitMQ AMQP 연결 문자열
    // 예: amqp://guest:guest@localhost:5672/
    URL string

    // 로컬 amqp:// 연결 명시적 허용
    AllowInsecureTransport bool

    ConsumerRetry  ConsumerRetryOptions
    PublisherRetry PublisherRetryOptions

    // 이벤트 소비 설정
    // nil이면 RabbitMQ 소비자를 활성화하지 않음
    Read *RabbitMqReadOptions

    // 이벤트 발행 설정
    // nil이면 RabbitMQ로 이벤트를 발행하지 않음
    Write *RabbitMqWriteOptions
}

type RabbitMqReadOptions struct {
    // 큐를 바인딩할 교환기 이름
    Exchange string
    PrefetchCount int
    FailurePolicy RabbitMqFailurePolicy
    DeadLetter *RabbitMqDeadLetterOptions

    // Deprecated: FailurePolicy 사용 권장
    RequeueOnError bool
}

type RabbitMqWriteOptions struct {
    // 이벤트를 발행할 교환기 이름
    Exchange string
}

失敗ポリシーと再試行のデフォルト値は、セキュリティ設定を参照してください。RequeueOnErrorは以前のバージョンとの互換性のための設定です。新しいコードではFailurePolicyを使用します。

例 ​

go
app.Run(boot.Options{
    Address: ":8080",
    RabbitMQ: &boot.RabbitMqOptions{
        URL:                    "amqp://guest:guest@localhost:5672/",
        AllowInsecureTransport: true, // 격리된 로컬 개발 환경 전용
        Read: &boot.RabbitMqReadOptions{
            Exchange: "stock-exchange",
        },
        Write: &boot.RabbitMqWriteOptions{
            Exchange: "stock-exchange",
        },
    },
    HTTP: &boot.HTTPOptions{},
})

イベント発行 ​

コントローラーでドメインイベントを収集し、登録済みのKafkaやRabbitMQのパブリッシャーに送信できます。

DomainEventインターフェース ​

go
import "github.com/NARUBROWN/spine/pkg/event/publish"

type DomainEvent interface {
    Name() string
    OccurredAt() time.Time
}

イベントの定義 ​

go
type OrderCreated struct {
    OrderID int64     `json:"order_id"`
    At      time.Time `json:"at"`
}

func (e OrderCreated) Name() string {
    return "order.created"
}

func (e OrderCreated) OccurredAt() time.Time {
    return e.At
}

コントローラーから発行 ​

go
import "github.com/NARUBROWN/spine/pkg/event/publish"

func (c *OrderController) Create(ctx context.Context, req *CreateOrderRequest) httpx.Response[Order] {
    order := c.repo.Save(req)

    // 이벤트 발행
    publish.Event(ctx, OrderCreated{
        OrderID: order.ID,
        At:      time.Now(),
    })

    return httpx.Response[Order]{Body: order}
}

publish.Event()は、context.Contextに接続されたイベントバスにイベントを収集します。コントローラーの実行とレスポンスの準備が成功すると、実行後フックで登録済みのKafkaまたはRabbitMQのパブリッシャーにまとめて送信します。この送信はBeforeResponseより前に実行されるため、DBトランザクションのコミットと原子的にまとめられるわけではありません。原子的な処理が必要な場合は、トランザクショナルアウトボックスを構成してください。

CLIプロジェクトでの使用 ​

最初のアプリを作成するで生成したプロジェクトでは、internal/application/application.goのBuildApplicationでアプリを構成し、ルートのmain.goでValidateのエラーを確認した後にRunを呼び出します。次のコマンドでプロジェクトを確認できます。

sh
spine doctor
spine contract check
spine verify

生成されるBuildApplicationの基本形は次のとおりです。

go
package application

import (
	"os"

	"github.com/NARUBROWN/spine"
	"github.com/NARUBROWN/spine/pkg/boot"
	"github.com/NARUBROWN/spine/pkg/httpx"
)

type HealthController struct{}

func NewHealthController() *HealthController { return &HealthController{} }

func (c *HealthController) Get() httpx.Response[map[string]string] {
	return httpx.Response[map[string]string]{Body: map[string]string{"status": "ok"}}
}

// BuildApplication은 서버 실행, 계약 검사와 오프라인 검증이 함께 사용하는 앱 구성 함수입니다.
func BuildApplication() (spine.App, boot.Options, error) {
	app := spine.New()
	app.Constructor(NewHealthController)
	app.Route("GET", "/health", (*HealthController).Get)

	address := ":8080"
	if port := os.Getenv("SPINE_PORT"); port != "" {
		address = ":" + port
	}
	return app, boot.Options{Address: address, HTTP: &boot.HTTPOptions{}}, nil
}

起動と終了の順序 ​

Runはオプションの事前検証後、依存性注入コンテナとイベントパブリッシャーを構成します。ユーザー定義のトランスポートのInitを呼び出し、HTTPとWebSocketのルートおよびインターセプターを解釈して、必要なコントローラーを事前に生成します。登録済みのコンシューマーがある場合は、ブローカーのランタイムも検証します。

この準備が終わると、コンシューマーとユーザー定義のトランスポートのStartを別のゴルーチンで実行し、最後にHTTPサーバーを起動します。HTTPルートは、同じメソッドでパスが曖昧に重なると起動に失敗します。たとえば、/users/:idと/users/meは重複するパスとして検出します。

終了時は、WebSocketとHTTPへのリクエストの受け入れを先に停止し、コンシューマーの停止を待ってから、ユーザー定義のトランスポートのStopを初期化の逆順に呼び出します。その後、イベントパブリッシャーのリソースを解放します。具体的な実装は、v0.5.1の起動および終了コードを参照してください。

制約とエラー ​

  • コンストラクタの戻り値は正確に1つである必要があります。(T, error)を返すコンストラクタは登録できません。
  • HTTP入力DTOはポインターで受け取り、成功レスポンスはhttpx.Response[T]など、サポートするレスポンス型で返します。コントローラーの書き方を参照してください。
  • Validateが成功しても、Runでコンストラクタ、依存関係、ルート、ブローカー接続のエラーが発生する場合があります。両方のメソッドの戻り値のエラーを確認してください。
  • WebSocketのパスを登録してHTTPを省略すると、WEBSOCKET_HTTP_REQUIRED設定エラーが発生します。
  • InterceptorForのスコープには、InterceptorHTTP、InterceptorWebSocket、InterceptorAllのいずれかを使用します。0やサポートされていないビットが含まれる場合は、INTERCEPTOR_SCOPE_INVALID設定エラーが発生します。
  • ネットワーク送信、イベント発行、DBコミットは、1つの原子的な操作ではありません。再試行や重複処理への対応が必要かどうかは、アプリで判断する必要があります。

関連情報 ​