diff --git a/apps/server/src/application/coordinators/coordinatorDeps.ts b/apps/server/src/application/coordinators/coordinatorDeps.ts index 1092676..d222a64 100644 --- a/apps/server/src/application/coordinators/coordinatorDeps.ts +++ b/apps/server/src/application/coordinators/coordinatorDeps.ts @@ -5,13 +5,12 @@ import type { CleanupGameRuntimePort, DisconnectRoomPort, - FindGameByPlayerPort, FindGameByRoomPort, FindRoomByIdPort, FindRoomByOwnerPort, - FindRoomByPlayerPort, RoomPhaseTransitionPort, } from "@server/domains/room/application/ports/roomUseCasePorts"; +import type { CoordinatorRuntimeDeps } from "./runtimeCoordinatorSupport"; /** START_GAME調停で利用する依存集合 */ export type StartGameCoordinatorDeps = { @@ -20,13 +19,10 @@ }; /** READY_FOR_GAME調停で利用する依存集合 */ -export type ReadyForGameCoordinatorDeps = { - roomManager: FindRoomByPlayerPort; - runtimeRegistry: FindGameByPlayerPort; -}; +export type ReadyForGameCoordinatorDeps = CoordinatorRuntimeDeps; /** DISCONNECT調停で利用する依存集合 */ export type DisconnectCoordinatorDeps = { - roomManager: DisconnectRoomPort & FindRoomByPlayerPort & FindRoomByIdPort; - runtimeRegistry: FindGameByPlayerPort & CleanupGameRuntimePort; + roomManager: DisconnectRoomPort & CoordinatorRuntimeDeps["roomManager"] & FindRoomByIdPort; + runtimeRegistry: CoordinatorRuntimeDeps["runtimeRegistry"] & CleanupGameRuntimePort; }; diff --git a/apps/server/src/application/coordinators/disconnectCoordinator.ts b/apps/server/src/application/coordinators/disconnectCoordinator.ts index acd27c8..b284eb2 100644 --- a/apps/server/src/application/coordinators/disconnectCoordinator.ts +++ b/apps/server/src/application/coordinators/disconnectCoordinator.ts @@ -7,9 +7,9 @@ } from "@server/domains/game/application/ports/gameUseCasePorts"; import type { RoomOutputPort } from "@server/domains/room/application/ports/roomUseCasePorts"; import type { DisconnectCoordinatorDeps } from "./coordinatorDeps"; -import { resolveRuntimeByPlayerId } from "@server/domains/room/application/services/RoomRuntimeResolver"; import { disconnectUseCase } from "@server/domains/game/application/useCases/disconnectUseCase"; import { roomDisconnectUseCase } from "@server/domains/room/application/useCases/roomDisconnectUseCase"; +import { resolveCoordinatorRuntime } from "./runtimeCoordinatorSupport"; /** 切断調停で利用する入力ポートと出力ポートの契約 */ export type DisconnectCoordinatorParams = { @@ -27,7 +27,13 @@ gameOutput, roomOutput, }: DisconnectCoordinatorParams) => { - const runtime = resolveRuntimeByPlayerId(roomManager, runtimeRegistry, socketId); + const runtime = resolveCoordinatorRuntime( + { + roomManager, + runtimeRegistry, + }, + socketId, + ); if (runtime) { disconnectUseCase({ diff --git a/apps/server/src/application/coordinators/readyForGameCoordinator.ts b/apps/server/src/application/coordinators/readyForGameCoordinator.ts index 897a981..8dc3a6f 100644 --- a/apps/server/src/application/coordinators/readyForGameCoordinator.ts +++ b/apps/server/src/application/coordinators/readyForGameCoordinator.ts @@ -6,8 +6,8 @@ type GameOutputPort, } from "@server/domains/game/application/ports/gameUseCasePorts"; import type { ReadyForGameCoordinatorDeps } from "./coordinatorDeps"; -import { resolveRuntimeByPlayerId } from "@server/domains/room/application/services/RoomRuntimeResolver"; import { readyForGameUseCase } from "@server/domains/game/application/useCases/readyForGameUseCase"; +import { resolveCoordinatorRuntime } from "./runtimeCoordinatorSupport"; type ReadyForGameCoordinatorParams = { socketId: string; @@ -22,7 +22,13 @@ runtimeRegistry, output, }: ReadyForGameCoordinatorParams) => { - const runtime = resolveRuntimeByPlayerId(roomManager, runtimeRegistry, socketId); + const runtime = resolveCoordinatorRuntime( + { + roomManager, + runtimeRegistry, + }, + socketId, + ); readyForGameUseCase({ socketId, diff --git a/apps/server/src/application/coordinators/runtimeCoordinatorSupport.ts b/apps/server/src/application/coordinators/runtimeCoordinatorSupport.ts new file mode 100644 index 0000000..f6fd183 --- /dev/null +++ b/apps/server/src/application/coordinators/runtimeCoordinatorSupport.ts @@ -0,0 +1,30 @@ +/** + * runtimeCoordinatorSupport + * コーディネータ層のランタイム解決処理を共通化する + */ +import type { + FindGameByPlayerPort, + FindRoomByPlayerPort, +} from "@server/domains/room/application/ports/roomUseCasePorts"; +import { + resolveRuntimeByPlayerId, + type RuntimeByPlayerResolution, +} from "@server/domains/room/application/services/RoomRuntimeResolver"; + +/** コーディネータで利用するランタイム解決依存集合 */ +export type CoordinatorRuntimeDeps = { + roomManager: FindRoomByPlayerPort; + runtimeRegistry: FindGameByPlayerPort; +}; + +/** コーディネータ向けにプレイヤーID起点ランタイムを解決する */ +export const resolveCoordinatorRuntime = ( + deps: CoordinatorRuntimeDeps, + socketId: string, +): RuntimeByPlayerResolution | undefined => { + return resolveRuntimeByPlayerId( + deps.roomManager, + deps.runtimeRegistry, + socketId, + ); +}; diff --git a/apps/server/src/domains/room/application/services/RoomRuntimeResolver.ts b/apps/server/src/domains/room/application/services/RoomRuntimeResolver.ts index 85d3723..c0b6fc2 100644 --- a/apps/server/src/domains/room/application/services/RoomRuntimeResolver.ts +++ b/apps/server/src/domains/room/application/services/RoomRuntimeResolver.ts @@ -31,3 +31,23 @@ gameManager, }; }; + +/** プレイヤーID起点のランタイム解決成功時のみ処理を実行する */ +export const runWithRuntimeByPlayerId = ( + roomResolver: FindRoomByPlayerPort, + runtimeResolver: FindGameByPlayerPort, + playerId: string, + onResolved: (resolution: RuntimeByPlayerResolution) => void, +): boolean => { + const runtime = resolveRuntimeByPlayerId( + roomResolver, + runtimeResolver, + playerId, + ); + if (!runtime) { + return false; + } + + onResolved(runtime); + return true; +}; diff --git a/apps/server/src/network/bootstrap/boot.ts b/apps/server/src/network/bootstrap/boot.ts index 1da296a..e95940d 100644 --- a/apps/server/src/network/bootstrap/boot.ts +++ b/apps/server/src/network/bootstrap/boot.ts @@ -3,18 +3,12 @@ * HTTPサーバにSocket.IOと各マネージャを接続して起動準備を行う */ import type { Server as HttpServer } from "http"; -import { RoomManager } from "@server/domains/room/RoomManager"; -import { RoomGameRuntimeRegistry } from "@server/domains/room/application/services/RoomGameRuntimeRegistry"; -import { SocketManager } from "../SocketManager"; -import { createIo } from "./createIo"; +import { createServerRuntime } from "./createServerRuntime"; /** 通信基盤とドメインマネージャを初期化して接続ハンドラを有効化する */ export const boot = (httpServer: HttpServer) => { // ネットワーク層とドメイン層の依存を構築する - const io = createIo(httpServer); - const roomManager = new RoomManager(); - const runtimeRegistry = new RoomGameRuntimeRegistry(roomManager); - const socketManager = new SocketManager(io, roomManager, runtimeRegistry); + const { socketManager } = createServerRuntime(httpServer); socketManager.initialize(); }; diff --git a/apps/server/src/network/bootstrap/createServerRuntime.ts b/apps/server/src/network/bootstrap/createServerRuntime.ts new file mode 100644 index 0000000..eb577e3 --- /dev/null +++ b/apps/server/src/network/bootstrap/createServerRuntime.ts @@ -0,0 +1,28 @@ +/** + * createServerRuntime + * サーバー起動時に必要な依存オブジェクト群を組み立てる + */ +import type { Server as HttpServer } from "http"; +import { RoomManager } from "@server/domains/room/RoomManager"; +import { RoomGameRuntimeRegistry } from "@server/domains/room/application/services/RoomGameRuntimeRegistry"; +import { SocketManager } from "@server/network/SocketManager"; +import { createIo } from "./createIo"; + +/** 起動時に構築する実行コンテキスト */ +export type ServerRuntime = { + socketManager: SocketManager; +}; + +/** HTTPサーバーから実行コンテキストを構築する */ +export const createServerRuntime = ( + httpServer: HttpServer, +): ServerRuntime => { + const io = createIo(httpServer); + const roomManager = new RoomManager(); + const runtimeRegistry = new RoomGameRuntimeRegistry(roomManager); + const socketManager = new SocketManager(io, roomManager, runtimeRegistry); + + return { + socketManager, + }; +}; diff --git a/apps/server/src/network/handlers/connectionEventLogger.ts b/apps/server/src/network/handlers/connectionEventLogger.ts new file mode 100644 index 0000000..f5ccc84 --- /dev/null +++ b/apps/server/src/network/handlers/connectionEventLogger.ts @@ -0,0 +1,25 @@ +/** + * connectionEventLogger + * 接続ハンドラで利用するログ記録処理を共通化する + */ +import { protocol } from "@repo/shared"; +import { logEvent } from "@server/logging/logger"; +import { logResults, logScopes } from "@server/logging/index"; + +/** CONNECTイベントの受信ログを記録する */ +export const logConnected = (socketId: string): void => { + logEvent(logScopes.NETWORK, { + event: protocol.SocketEvents.CONNECT, + result: logResults.CONNECTED, + socketId, + }); +}; + +/** DISCONNECTイベントの受信ログを記録する */ +export const logDisconnected = (socketId: string): void => { + logEvent(logScopes.NETWORK, { + event: protocol.SocketEvents.DISCONNECT, + result: logResults.DISCONNECTED, + socketId, + }); +}; diff --git a/apps/server/src/network/handlers/createOutputAdapters.ts b/apps/server/src/network/handlers/createOutputAdapters.ts new file mode 100644 index 0000000..2547214 --- /dev/null +++ b/apps/server/src/network/handlers/createOutputAdapters.ts @@ -0,0 +1,52 @@ +/** + * createOutputAdapters + * ソケット接続単位と切断処理単位の出力アダプタ生成を共通化する + */ +import type { Server, Socket } from "socket.io"; +import { createCommonHandlerContext } from "./CommonHandler"; +import { + createGameDisconnectOutputAdapter, + createGameOutputAdapter, + type GameDisconnectOutputAdapter, + type GameOutputAdapter, +} from "./game/createGameOutputAdapter"; +import { + createRoomDisconnectOutputAdapter, + createRoomOutputAdapter, + type RoomOutputAdapter, +} from "./room/createRoomOutputAdapter"; + +/** 接続単位で利用するゲームとルームの出力アダプタ集合 */ +export type SocketOutputAdapters = { + game: GameOutputAdapter; + room: RoomOutputAdapter; +}; + +/** 切断処理で利用するゲームとルームの出力アダプタ集合 */ +export type DisconnectOutputAdapters = { + game: GameDisconnectOutputAdapter; + room: Pick; +}; + +/** 接続ソケット向けの出力アダプタを生成する */ +export const createSocketOutputAdapters = ( + io: Server, + socket: Socket, +): SocketOutputAdapters => { + const common = createCommonHandlerContext(io, socket); + + return { + game: createGameOutputAdapter(common), + room: createRoomOutputAdapter(common), + }; +}; + +/** 切断処理向けの出力アダプタを生成する */ +export const createDisconnectOutputAdapters = ( + io: Server, +): DisconnectOutputAdapters => { + return { + game: createGameDisconnectOutputAdapter(io), + room: createRoomDisconnectOutputAdapter(io), + }; +}; diff --git a/apps/server/src/network/handlers/game/gameEventOrchestrators.ts b/apps/server/src/network/handlers/game/gameEventOrchestrators.ts new file mode 100644 index 0000000..33d0ee9 --- /dev/null +++ b/apps/server/src/network/handlers/game/gameEventOrchestrators.ts @@ -0,0 +1,135 @@ +/** + * gameEventOrchestrators + * ゲーム受信イベントごとの調停処理を提供する + * 受信ハンドラからユースケース実行責務を分離する + */ +import { protocol, type BombHitReportPayload, type PingPayload, type PlaceBombPayload, type playerTypes } from "@repo/shared"; +import { readyForGameCoordinator } from "@server/application/coordinators/readyForGameCoordinator"; +import { startGameCoordinator } from "@server/application/coordinators/startGameCoordinator"; +import { movePlayerUseCase } from "@server/domains/game/application/useCases/movePlayerUseCase"; +import { pingUseCase } from "@server/domains/game/application/useCases/pingUseCase"; +import { placeBombUseCase } from "@server/domains/game/application/useCases/placeBombUseCase"; +import { reportBombHitUseCase } from "@server/domains/game/application/useCases/reportBombHitUseCase"; +import { runWithRuntimeByPlayerId } from "@server/domains/room/application/services/RoomRuntimeResolver"; +import type { GameOutputAdapter } from "./createGameOutputAdapter"; +import type { + GameHandlerRoomPort, + GameHandlerRuntimePort, +} from "@server/network/types/connectionPorts"; + +/** START_GAMEイベントの入力ペイロード型 */ +export type StartGamePayload = { + targetPlayerCount?: number; +}; + +/** ゲームイベント調停で利用する依存集合 */ +export type GameEventOrchestratorDeps = { + socketId: string; + roomManager: GameHandlerRoomPort; + runtimeRegistry: GameHandlerRuntimePort; + output: GameOutputAdapter; +}; + +/** PINGイベントを調停してPONG返却ユースケースを実行する */ +export const handlePingEvent = ( + deps: GameEventOrchestratorDeps, + clientTime: PingPayload, +): void => { + pingUseCase({ + clientTime, + output: deps.output, + }); +}; + +/** START_GAMEイベントを調停してゲーム開始ユースケースを起動する */ +export const handleStartGameEvent = ( + deps: GameEventOrchestratorDeps, + payload: StartGamePayload, +): void => { + startGameCoordinator({ + ownerId: deps.socketId, + requestedPlayerCount: payload.targetPlayerCount, + roomManager: deps.roomManager, + runtimeRegistry: deps.runtimeRegistry, + output: deps.output, + }); +}; + +/** READY_FOR_GAMEイベントを調停して準備状態通知ユースケースを実行する */ +export const handleReadyForGameEvent = ( + deps: GameEventOrchestratorDeps, +): void => { + readyForGameCoordinator({ + socketId: deps.socketId, + roomManager: deps.roomManager, + runtimeRegistry: deps.runtimeRegistry, + output: deps.output, + }); +}; + +/** MOVEイベントを調停して移動ユースケースを実行する */ +export const handleMoveEvent = ( + deps: GameEventOrchestratorDeps, + move: playerTypes.MovePayload, +): void => { + runWithRuntimeByPlayerId( + deps.roomManager, + deps.runtimeRegistry, + deps.socketId, + ({ gameManager }) => { + movePlayerUseCase({ + gameManager, + playerId: deps.socketId, + move, + }); + }, + ); +}; + +/** PLACE_BOMBイベントを調停して爆弾設置ユースケースを実行する */ +export const handlePlaceBombEvent = ( + deps: GameEventOrchestratorDeps, + payload: PlaceBombPayload, +): void => { + runWithRuntimeByPlayerId( + deps.roomManager, + deps.runtimeRegistry, + deps.socketId, + ({ roomId, gameManager }) => { + placeBombUseCase({ + roomId, + bombStore: gameManager, + input: { + socketId: deps.socketId, + payload, + nowMs: Date.now(), + }, + output: deps.output, + }); + }, + ); +}; + +/** BOMB_HIT_REPORTイベントを調停して被弾報告ユースケースを実行する */ +export const handleBombHitReportEvent = ( + deps: GameEventOrchestratorDeps, + payload: BombHitReportPayload, +): void => { + runWithRuntimeByPlayerId( + deps.roomManager, + deps.runtimeRegistry, + deps.socketId, + ({ roomId, gameManager }) => { + reportBombHitUseCase({ + roomId, + validation: gameManager, + input: { + socketId: deps.socketId, + payload, + nowMs: Date.now(), + }, + output: deps.output, + }); + }, + ); +}; diff --git a/apps/server/src/network/handlers/game/registerGameHandlers.ts b/apps/server/src/network/handlers/game/registerGameHandlers.ts index 4934d91..8bab813 100644 --- a/apps/server/src/network/handlers/game/registerGameHandlers.ts +++ b/apps/server/src/network/handlers/game/registerGameHandlers.ts @@ -2,23 +2,12 @@ * registerGameHandlers * ゲーム関連イベントの受信ハンドラを登録する */ -import { Server, Socket } from "socket.io"; +import { Socket } from "socket.io"; import { protocol } from "@repo/shared"; -import { readyForGameCoordinator } from "@server/application/coordinators/readyForGameCoordinator"; -import { startGameCoordinator } from "@server/application/coordinators/startGameCoordinator"; import type { - FindGameByRoomPort, - FindGameByPlayerPort, - FindRoomByOwnerPort, - FindRoomByPlayerPort, - RoomPhaseTransitionPort, -} from "@server/domains/room/application/ports/roomUseCasePorts"; -import { movePlayerUseCase } from "@server/domains/game/application/useCases/movePlayerUseCase"; -import { placeBombUseCase } from "@server/domains/game/application/useCases/placeBombUseCase"; -import { pingUseCase } from "@server/domains/game/application/useCases/pingUseCase"; -import { reportBombHitUseCase } from "@server/domains/game/application/useCases/reportBombHitUseCase"; -import { resolveRuntimeByPlayerId } from "@server/domains/room/application/services/RoomRuntimeResolver"; -import { createCommonHandlerContext } from "@server/network/handlers/CommonHandler"; + GameHandlerRoomPort, + GameHandlerRuntimePort, +} from "@server/network/types/connectionPorts"; import { isBombHitReportPayload, isMovePayload, @@ -28,7 +17,15 @@ } from "@server/network/validation/socketPayloadValidators"; import { createServerSocketOnBridge } from "@server/network/handlers/socketEventBridge"; import { createPayloadGuard } from "@server/network/handlers/payloadGuard"; -import { createGameOutputAdapter } from "./createGameOutputAdapter"; +import type { GameOutputAdapter } from "./createGameOutputAdapter"; +import { + handleBombHitReportEvent, + handleMoveEvent, + handlePingEvent, + handlePlaceBombEvent, + handleReadyForGameEvent, + handleStartGameEvent, +} from "./gameEventOrchestrators"; /** ゲーム受信イベントごとの入力検証関数を保持するテーブル */ const gamePayloadValidators = { @@ -40,15 +37,11 @@ /** ゲームイベントの購読とユースケース呼び出しを設定する */ export const registerGameHandlers = ( - io: Server, socket: Socket, - roomManager: FindRoomByOwnerPort & - FindRoomByPlayerPort & - RoomPhaseTransitionPort, - runtimeRegistry: FindGameByRoomPort & FindGameByPlayerPort, + roomManager: GameHandlerRoomPort, + runtimeRegistry: GameHandlerRuntimePort, + gameOutputAdapter: GameOutputAdapter, ) => { - const common = createCommonHandlerContext(io, socket); - const gameOutputAdapter = createGameOutputAdapter(common); const { onEvent } = createServerSocketOnBridge(socket); const { guardOnEvent } = createPayloadGuard(socket.id); const guardPingPayload = guardOnEvent( @@ -73,10 +66,15 @@ return; } - pingUseCase({ + handlePingEvent( + { + socketId: socket.id, + roomManager, + runtimeRegistry, + output: gameOutputAdapter, + }, clientTime, - output: gameOutputAdapter, - }); + ); }); // オーナー開始要求に応じてゲーム進行ユースケースを起動する @@ -85,18 +83,20 @@ return; } - startGameCoordinator({ - ownerId: socket.id, - requestedPlayerCount: data.targetPlayerCount, - roomManager, - runtimeRegistry, - output: gameOutputAdapter, - }); + handleStartGameEvent( + { + socketId: socket.id, + roomManager, + runtimeRegistry, + output: gameOutputAdapter, + }, + data, + ); }); // 参加者の準備完了通知を受けて現在状態を返す onEvent(protocol.SocketEvents.READY_FOR_GAME, () => { - readyForGameCoordinator({ + handleReadyForGameEvent({ socketId: socket.id, roomManager, runtimeRegistry, @@ -110,20 +110,15 @@ return; } - const runtime = resolveRuntimeByPlayerId( - roomManager, - runtimeRegistry, - socket.id, + handleMoveEvent( + { + socketId: socket.id, + roomManager, + runtimeRegistry, + output: gameOutputAdapter, + }, + data, ); - if (!runtime) { - return; - } - - movePlayerUseCase({ - gameManager: runtime.gameManager, - playerId: socket.id, - move: data, - }); }); // 爆弾設置入力を検証し,所属ルームへ同期配信する @@ -132,25 +127,15 @@ return; } - const runtime = resolveRuntimeByPlayerId( - roomManager, - runtimeRegistry, - socket.id, - ); - if (!runtime) { - return; - } - - placeBombUseCase({ - roomId: runtime.roomId, - bombStore: runtime.gameManager, - input: { + handlePlaceBombEvent( + { socketId: socket.id, - payload: data, - nowMs: Date.now(), + roomManager, + runtimeRegistry, + output: gameOutputAdapter, }, - output: gameOutputAdapter, - }); + data, + ); }); // 被弾報告を受信する @@ -159,24 +144,14 @@ return; } - const runtime = resolveRuntimeByPlayerId( - roomManager, - runtimeRegistry, - socket.id, - ); - if (!runtime) { - return; - } - - reportBombHitUseCase({ - roomId: runtime.roomId, - validation: runtime.gameManager, - input: { + handleBombHitReportEvent( + { socketId: socket.id, - payload: data, - nowMs: Date.now(), + roomManager, + runtimeRegistry, + output: gameOutputAdapter, }, - output: gameOutputAdapter, - }); + data, + ); }); }; diff --git a/apps/server/src/network/handlers/registerConnectionHandlers.ts b/apps/server/src/network/handlers/registerConnectionHandlers.ts index e4fccca..518b5d3 100644 --- a/apps/server/src/network/handlers/registerConnectionHandlers.ts +++ b/apps/server/src/network/handlers/registerConnectionHandlers.ts @@ -5,12 +5,13 @@ import { Server, Socket } from "socket.io"; import { protocol } from "@repo/shared"; import { disconnectCoordinator } from "@server/application/coordinators/disconnectCoordinator"; -import { logEvent } from "@server/logging/logger"; -import { logResults, logScopes } from "@server/logging/index"; import { registerGameHandlers } from "./GameHandler"; import { registerRoomHandlers } from "./RoomHandler"; -import { createGameDisconnectOutputAdapter } from "./game/createGameOutputAdapter"; -import { createRoomDisconnectOutputAdapter } from "./room/createRoomOutputAdapter"; +import { + createDisconnectOutputAdapters, + createSocketOutputAdapters, +} from "./createOutputAdapters"; +import { logConnected, logDisconnected } from "./connectionEventLogger"; import type { RegisterConnectionHandlersParams, } from "../types/connectionPorts"; @@ -21,34 +22,37 @@ roomManager, runtimeRegistry, }: RegisterConnectionHandlersParams) => { - const gameDisconnectOutputAdapter = createGameDisconnectOutputAdapter(io); - const roomDisconnectOutputAdapter = createRoomDisconnectOutputAdapter(io); + const disconnectOutputAdapters = createDisconnectOutputAdapters(io); io.on(protocol.SocketEvents.CONNECT, (socket: Socket) => { - // 接続ログを記録してドメイン別ハンドラを登録する - logEvent(logScopes.NETWORK, { - event: protocol.SocketEvents.CONNECT, - result: logResults.CONNECTED, - socketId: socket.id, - }); + const socketOutputAdapters = createSocketOutputAdapters(io, socket); - registerRoomHandlers(io, socket, roomManager, runtimeRegistry); - registerGameHandlers(io, socket, roomManager, runtimeRegistry); + // 接続ログを記録してドメイン別ハンドラを登録する + logConnected(socket.id); + + registerRoomHandlers( + socket, + roomManager, + runtimeRegistry, + socketOutputAdapters.room, + ); + registerGameHandlers( + socket, + roomManager, + runtimeRegistry, + socketOutputAdapters.game, + ); socket.on(protocol.SocketEvents.DISCONNECT, () => { // 切断ログ記録後にドメイン別の後処理を実行する - logEvent(logScopes.NETWORK, { - event: protocol.SocketEvents.DISCONNECT, - result: logResults.DISCONNECTED, - socketId: socket.id, - }); + logDisconnected(socket.id); disconnectCoordinator({ socketId: socket.id, roomManager, runtimeRegistry, - gameOutput: gameDisconnectOutputAdapter, - roomOutput: roomDisconnectOutputAdapter, + gameOutput: disconnectOutputAdapters.game, + roomOutput: disconnectOutputAdapters.room, }); }); }); diff --git a/apps/server/src/network/handlers/room/registerRoomHandlers.ts b/apps/server/src/network/handlers/room/registerRoomHandlers.ts index ca247cb..a893f3c 100644 --- a/apps/server/src/network/handlers/room/registerRoomHandlers.ts +++ b/apps/server/src/network/handlers/room/registerRoomHandlers.ts @@ -2,20 +2,17 @@ * registerRoomHandlers * ルーム参加イベントの受信ハンドラを登録する */ -import { Server, Socket } from "socket.io"; +import { Socket } from "socket.io"; import { protocol } from "@repo/shared"; import type { - EnsureGameRuntimePort, - JoinRoomPort, -} from "@server/domains/room/application/ports/roomUseCasePorts"; -import { joinRoomUseCase } from "@server/domains/room/application/useCases/joinRoomUseCase"; -import { logEvent } from "@server/logging/logger"; -import { logResults, logScopes } from "@server/logging/index"; -import { createCommonHandlerContext } from "@server/network/handlers/CommonHandler"; + RoomHandlerRoomPort, + RoomHandlerRuntimePort, +} from "@server/network/types/connectionPorts"; import { createPayloadGuard } from "@server/network/handlers/payloadGuard"; import { createServerSocketOnBridge } from "@server/network/handlers/socketEventBridge"; import { isJoinRoomPayload } from "@server/network/validation/socketPayloadValidators"; -import { createRoomOutputAdapter } from "./createRoomOutputAdapter"; +import type { RoomOutputAdapter } from "./createRoomOutputAdapter"; +import { handleJoinRoomEvent } from "./roomEventOrchestrators"; /** ルーム受信イベントごとの入力検証関数を保持するテーブル */ const roomPayloadValidators = { @@ -24,13 +21,11 @@ /** ルーム参加イベントを検証して参加ユースケースへ連携する */ export const registerRoomHandlers = ( - io: Server, socket: Socket, - roomManager: JoinRoomPort, - runtimeRegistry: EnsureGameRuntimePort + roomManager: RoomHandlerRoomPort, + runtimeRegistry: RoomHandlerRuntimePort, + roomOutputAdapter: RoomOutputAdapter, ) => { - const common = createCommonHandlerContext(io, socket); - const roomOutputAdapter = createRoomOutputAdapter(common); const { onEvent } = createServerSocketOnBridge(socket); const { guardOnEvent } = createPayloadGuard(socket.id); const guardJoinRoomPayload = guardOnEvent( @@ -44,51 +39,17 @@ return; } - const { roomId } = data; - - const joinResult = joinRoomUseCase({ - roomManager, - runtimeRegistry, - socketId: socket.id, + await handleJoinRoomEvent( + { + socketId: socket.id, + roomManager, + runtimeRegistry, + output: roomOutputAdapter, + joinRoom: async (roomId) => { + await socket.join(roomId); + }, + }, data, - output: roomOutputAdapter, - }); - - // 参加拒否時は理由を通知する - switch (joinResult.status) { - case "full": - logEvent(logScopes.NETWORK, { - event: protocol.SocketEvents.JOIN_ROOM, - result: logResults.REJECTED_ROOM_FULL, - roomId, - socketId: socket.id, - }); - return; - - case "duplicate": - logEvent(logScopes.NETWORK, { - event: protocol.SocketEvents.JOIN_ROOM, - result: logResults.REJECTED_DUPLICATE, - roomId, - socketId: socket.id, - }); - return; - - case "joined": - await socket.join(roomId); - roomOutputAdapter.publishRoomUpdateToRoom(roomId, joinResult.room); - logEvent(logScopes.ROOM_USE_CASE, { - event: protocol.SocketEvents.ROOM_UPDATE, - result: logResults.EMITTED, - roomId, - socketId: socket.id, - ownerId: joinResult.room.ownerId, - totalPlayers: joinResult.room.players.length, - }); - return; - - default: - return; - } + ); }); }; diff --git a/apps/server/src/network/handlers/room/roomEventOrchestrators.ts b/apps/server/src/network/handlers/room/roomEventOrchestrators.ts new file mode 100644 index 0000000..7c06a89 --- /dev/null +++ b/apps/server/src/network/handlers/room/roomEventOrchestrators.ts @@ -0,0 +1,73 @@ +/** + * roomEventOrchestrators + * ルーム受信イベントごとの調停処理を提供する + * 受信ハンドラからユースケース実行責務を分離する + */ +import type { roomTypes } from "@repo/shared"; +import { joinRoomUseCase } from "@server/domains/room/application/useCases/joinRoomUseCase"; +import { logEvent } from "@server/logging/logger"; +import { logResults, logScopes, roomUseCaseLogEvents } from "@server/logging/index"; +import type { + RoomHandlerRoomPort, + RoomHandlerRuntimePort, +} from "@server/network/types/connectionPorts"; +import type { RoomOutputAdapter } from "./createRoomOutputAdapter"; + +/** JOIN_ROOMイベント調停で利用する依存集合 */ +export type JoinRoomOrchestratorDeps = { + socketId: string; + roomManager: RoomHandlerRoomPort; + runtimeRegistry: RoomHandlerRuntimePort; + output: RoomOutputAdapter; + joinRoom: (roomId: string) => Promise; +}; + +/** JOIN_ROOMイベントを調停して参加ユースケースを実行する */ +export const handleJoinRoomEvent = async ( + deps: JoinRoomOrchestratorDeps, + payload: roomTypes.JoinRoomPayload, +): Promise => { + const joinResult = joinRoomUseCase({ + roomManager: deps.roomManager, + runtimeRegistry: deps.runtimeRegistry, + socketId: deps.socketId, + data: payload, + output: deps.output, + }); + + switch (joinResult.status) { + case "full": + logEvent(logScopes.NETWORK, { + event: roomUseCaseLogEvents.JOIN_ROOM, + result: logResults.REJECTED_ROOM_FULL, + roomId: payload.roomId, + socketId: deps.socketId, + }); + return; + + case "duplicate": + logEvent(logScopes.NETWORK, { + event: roomUseCaseLogEvents.JOIN_ROOM, + result: logResults.REJECTED_DUPLICATE, + roomId: payload.roomId, + socketId: deps.socketId, + }); + return; + + case "joined": + await deps.joinRoom(payload.roomId); + deps.output.publishRoomUpdateToRoom(payload.roomId, joinResult.room); + logEvent(logScopes.ROOM_USE_CASE, { + event: roomUseCaseLogEvents.ROOM_UPDATE, + result: logResults.EMITTED, + roomId: payload.roomId, + socketId: deps.socketId, + ownerId: joinResult.room.ownerId, + totalPlayers: joinResult.room.players.length, + }); + return; + + default: + return; + } +}; diff --git a/apps/server/src/network/types/connectionPorts.ts b/apps/server/src/network/types/connectionPorts.ts index 49454e8..79a503f 100644 --- a/apps/server/src/network/types/connectionPorts.ts +++ b/apps/server/src/network/types/connectionPorts.ts @@ -32,6 +32,27 @@ & FindGameByRoomPort & FindGameByPlayerPort; +/** ゲーム受信ハンドラで利用するルーム依存ポート */ +export type GameHandlerRoomPort = Pick< + ConnectionRoomPort, + "getRoomByOwnerId" | "getRoomByPlayerId" | "markRoomPlaying" | "markRoomWaiting" +>; + +/** ゲーム受信ハンドラで利用するランタイム依存ポート */ +export type GameHandlerRuntimePort = Pick< + ConnectionRuntimePort, + "getGameManagerByRoomId" | "getGameManagerByPlayerId" +>; + +/** ルーム受信ハンドラで利用するルーム依存ポート */ +export type RoomHandlerRoomPort = Pick; + +/** ルーム受信ハンドラで利用するランタイム依存ポート */ +export type RoomHandlerRuntimePort = Pick< + ConnectionRuntimePort, + "ensureGameManagerForRoom" +>; + /** ソケット接続全体で利用するルーム管理ポート集合 */ export type SocketConnectionRoomPort = & ConnectionRoomPort