Resource / Operation / Permission / Module / Transportを分離すると入口が増えてもCoreを保ちやすい
小規模なWeb Applicationでは、次の4つがほぼ同じ場所にあっても成立しやすい。
HTTP Route
= Resource Resolution
= Authorization Boundary
= Application Structure
Route数が少なく、外部入口がHTTPだけなら、これはむしろ高い局所性を持つ。問題はApplicationが中規模化し、REST以外にMCP、Job、CLI、内部呼び出しが増えたときである。同じCapabilityが複数の入口から呼ばれると、HTTP routeをApplication Coreの基本単位にした構造ではBusiness Logic・認可・ownershipがTransportへ引きずられやすい。
そこで概念を次のように分けて考えられる。
- Resource: Domain上で安定して参照したいIdentity。Resource IdentityはURLそのものではなくDomain上の安定した参照として考える
- Operation: Applicationが提供するCapability。OperationはTransportから独立したApplication Capabilityとして置ける
- Permission: PrincipalがActionをResourceへ行えるかという認可語彙。PermissionはActionとResourceを結び付けるApplication側の認可語彙 / PARCは認可問い合わせをPrincipal / Action / Resource / Contextへ分解する
- Module: CapabilityとDomain語彙をownershipする境界。Module BoundaryはPublic API・Internal・Allowed Dependenciesで実体化する
- Transport: HTTP、MCP、Job、CLIなどOperationを外部へ公開・起動する方法。Transport AdapterはHTTPやMCPをApplication Operationへ翻訳する境界
全体像は次のようになる。
HTTP / MCP / Job / CLI
↓
Transport Adapter
↓
Application Operation
↓
Authorization
↓
Module
↓
Domain / Repository
この構造ではRESTを廃止しない。RESTは捨てるのではなくApplication CoreからAdapterへ降格させる としてResource-orientedなHTTP APIを外側へ残し、Google AIPの標準Methodやcustom methodの規約を活用できる。GoaがService MethodとHTTP mappingを分け、Goa-AIが同じdesign vocabularyからMCP Toolを扱う構造は、この分離を考える参考になる。MCP 2026-07-28仕様自身もprotocol semanticsとtransport bindingを分け、HTTP authorizationをtransport-level capabilityとして定義している。
認可も一枚岩にしない。OAuth/MCP scopeなどのTransport Authorizationは接続・Token・Scopeを守るがDomain Permissionの代わりではない と、具体ResourceへのDomain AuthorizationはOperationが具体Resourceへ作用してよいかを判定する を区別できる。CedarのPARCは後者の問い合わせ形として使いやすく、OpenFGA/SpiceDBのrelationship modelはその判定に必要なauthority graphを表せる。ただしResource HierarchyはOwnershipやScopingを表せるが万能の認可Treeではない と Authorization GraphはResource Hierarchyとは別のRelationship Graphとして持つ は同一ではない。Domain上のparent-child treeだけでは共有・membership・delegationなどを表しきれない。
さらに、単一Resourceへの Can() だけを共通抽象にするのも十分ではない。List Authorizationは単一ResourceへのCan()の繰り返しでは設計しきれない や Search Authorizationは検索集合と認可集合のIntersection問題になる では「候補集合と認可集合をどうintersectionするか」が問題になり、Lookup、Bulk Check、projection/materializationなど集合向けの戦略が必要になる。
Resourceも万能化しない方がよい。Generic Resourceへ寄せすぎるとDomain Semanticsが失われる の通り、境界では type + id のGeneric ResourceRefが便利でも、Application Coreまで全Domain objectを共通Treeへ押し込むとlifecycleや不変条件などのDomain Semanticsを失う。Resource-oriented designは強力なAPI vocabularyだが、Domain modelingの万能理論ではない。
Application StructureについてはModular Monolithは単一Deployableの中に検証可能なModule境界を持つ と Vertical SliceはLayerではなくUse Caseの変更軸に沿ってコードをまとめる を別スケールで組み合わせられる。ModuleにはPublic API・Internal implementation・Allowed Dependenciesが必要で、単なるdirectory分割ではない。そのModule内部ではOperationごとのVertical Sliceを使い、不要なhorizontal Layerを減らせる。
この点はAI coding agentにも関係する。RepoCoderやAgentlessが示すようにrepository-level codingではlocalization/retrievalが重要であり、Module BoundaryはAI Coding AgentのContext Boundaryとしても機能しうる としてModule ownershipを使える可能性がある。ただしLayerやdispatch abstractionを増やすほどLocalization CostはLayerと抽象化を増やすほど無視できなくなる も増える。Application CatalogはArchitectureをAIとToolが読めるMachine-readable Dataにする でModule、Operation、Resource、Permission、Transport mappingを機械可読にするのは一案だが、Architectureを複雑にしてAIへ説明すること自体を目的にしてはいけない。
同じ理由でCommandはHTTP Requestではなく状態変更の意図を表す、Command BusはOperation dispatchの選択肢でありArchitectureの前提ではない、CQRSはRead ModelとWrite Modelの分離でありCommand BusやEvent Sourcingの同義語ではない はこの構造の必須要素ではない。直接Operationを呼ぶだけで十分ならその方が単純である。要求のmessage化、共通dispatch pipeline、read/write modelの分離が実際に必要になった時点で導入すればよい。
したがって、この分離は「小規模Applicationにも最初から5種類の抽象を実装する」という処方箋ではない。むしろ、HTTP routeへ重なっていた複数の責務を、規模と入口の増加に応じて別々に動かせるよう名前を与えるための概念整理である。
小規模では重ねたままにし、中規模化して変更理由が分かれ始めたところだけを切り離す。その方が、Transport independenceとDomain semanticsを得ながら、人間とAIの双方にとっての局所性を残しやすい。