AI を活用して“少人数開発”をやり切る方法 ─ NEWT Partner Hub の開発裏側

published
published
author
authorDisplayName
Masato Kita
category
backend
AI
mainImage
20251203_kita_001.png
publishedAt
Dec 3, 2025
slug
Advent-Calendar-20251203
tags
advent calendar
backend
AI
notion image
 
💡
NEWT Product Advent Calendar 2025」3日目は、令和トラベル エンジニアのkitaが、Head of Engineering Office miisanからバトンを受け取り執筆します。ぜひ、最後までご覧ください!
 
こんにちは!今回は、2025年1月から9月にかけて実施した、ホテル事業の基盤を支えるプロダクト「NEWT Partner Hub」の “ゼロイチ” 開発についてお話しします。
このプロジェクトでは、「希望的観測による見積もりの甘さ」があり、技術的には、「仕様が複雑化しやすい外部システムとのAPI連携」という高い壁に盛大にぶつかりました。
 
この記事では、2.5ヶ月のリリース遅延という苦い「失敗」と、堅牢で運用しやすいシステムを生み出す鍵となった「ドメイン駆動設計(DDD)とクリーンアーキテクチャ」の成功体験など、できる限り “リアル” にお届けします。ぜひ最後までご覧ください!
 

1. プロジェクトの背景:なぜ「内製での新規開発」が、いま必要だったのか 🤔

旅行アプリ『NEWT(ニュート)』は、外部予約サービスとAPI接続することで、ホテルの予約サービスを実現していました。
しかし、宿泊事業を第二の柱として強化するにあたって、宿泊施設さまと令和トラベルが直接契約することで、品揃えの強化や収益性を改善できる仕組みを構築することが重要命題で、そのためには宿泊施設さまがNEWTに掲載・予約管理を行う管理画面の構築が必要不可欠でした。
 
またそれ以外にも、他社サービスの仕様に依存することで、柔軟で高速な検索機能がつくれない、料金カレンダーもつくれない、など、理想的なカスタマー体験が実現できないという問題もありました。
 

乗り越えるべき「3つの壁」

しかし、それは単なる機能追加のレベルではありません。以下の3つの難題がありました。
 
  1. 他社サービスAPI に依存した実装(技術的課題)
    1. 当時の既存コードは「モジュラーモノリス」構成で、モジュール間の依存関係が複雑化していました。特に外部サービス API に強く依存しており、機能追加の影響範囲が読みにくく、スケールさせづらい問題がありました。
       
  1. 仕様が複雑化しやすい外部システムとのAPI連携(技術的課題)
    1. 管理画面を内製で開発するといっても「サイトコントローラー(宿泊施設の在庫管理システム)」との接続は必須です。仕様が複雑化しやすい外部システムと、いかに低レイテンシかつ堅牢に連携できるかが問われます。
      また、将来的に複数のサイトコントローラーと接続することを想定した依存性の少ないシステム設計が重要となります。
       
  1. レッドオーシャン、後発OTAとしてのUX差別化(ビジネス課題)
    1. 後発OTA(Online Travel Agent)として参入するにあたり、使いづらいシステムでは運用してもらえません。
      継続して利用いただけるように、最小スコープでのリリースを目指すと言いつつ、管理画面は極限まで簡略化し、AIエージェントによる自動化など差別化要素を盛り込む必要がありました。
       
プロジェクト全体の振り返りについては以下の対談記事もぜひご覧ください!
▼『ゼロから作り上げた、半年超の大型プロジェクトにおける挑戦と学び』
 

2. PMとしての失敗談:「希望的観測」という名の落とし穴 🕳️

正直に告白します。プロジェクトは当初の計画より2.5ヶ月遅れでのリリースとなりました。
プロジェクトリードを担当していた私の、当時の見積もりや進行管理には、今振り返ると冷や汗が出るような問題点がありました。
 

なぜ遅れたのか?(あるある失敗集)

「テトリス」のようなスケジュール管理
リリース目標ありきで、タスクを隙間なく詰め込んでいましたが、タスク間の依存関係が整理しきれておらず、1つの遅れが全体に波及しました。
 
「AIへの過度な期待」バイアス
「類似タスクはAIを使えば爆速で終わるはず」という希望的観測が見積もりに含まれていましたが、実際にはドメイン知識の習得や細部の調整に時間がかかりました。
 
リスクバッファの欠如
ドメインエキスパートが不在の中、自分たちで仕様を研究しながら実装する体制であったにもかかわらず、調査工数や手戻りのリスクを見込んでいませんでした。
テトリスのように各タスクをアサイン→伸びる→ずれる…
テトリスのように各タスクをアサイン→伸びる→ずれる…
 

立て直しとチームの成長

「6月末リリースは不可能」と判断した時点から、全体のプロジェクトリードは他のメンバーに任せ、私はサイトコントローラー接続のクリティカルパスの解像度を上げることに専念しました。
 
個人的には非常に悔しい結果となりましたが、チームとしては「Badな状況でも隠さず共有し、全員で解決策を考える」文化が定着しました。
振り返り(KPT)を随時アップデートし、小さなGoodでも称賛しあうことで、チームは崩壊せず走り切ることができました。
notion image
 

3. 技術的な成功体験:チームを救ったアーキテクチャ 🏗️

マネジメントで苦戦する一方、技術面、特にアーキテクチャ設計においては、開発効率と品質を高めることに成功しました。
 
複雑な業務要件と、外部依存とならない柔軟なシステムをどう整理したのか。ここからは具体的な設計の話をします。
 

14個に分割された「境界付けられたコンテキスト」

notion image
 
ホテル管理業務を、ドメイン駆動設計(DDD)に基づき14個のコンテキストに分割しました。
💡
  • コアビジネス:
    • property-management (施設管理), inventory (在庫), pricing (料金), reservation (予約) など
  • 統合・連携:
    • channel-integration (サイトコントローラー連携)など
  • サポート:
    • access-control (認証), shared-kernel (共通概念)など
 
この分割により、「在庫の話をしているときは、料金のことを考えなくていい」という明確な責務分離が実現しました。また、shared-kernelMoneyStayDate といった共通のValue Objectを定義することで、システム全体の一貫性を保っています。
 

クリーンアーキテクチャによる実装

各コンテキスト内は、クリーンアーキテクチャの4層構造で統一されています。
contexts/[コンテキスト名]/ ├── domain/ # ドメインロジック (Entity, Repository Interface) ├── application/ # ユースケース ├── infrastructure/ # 技術的詳細 (DB実装, 外部APIクライアント) └── public/ # 他コンテキストへの公開Facade
 
この設計がもたらしたメリット:
💡
  • 並行開発:
    • コンテキスト単位でチームを分けられるため、他のチームの実装を待つ時間が減りました。
  • AIフレンドリー:
    • 構造が統一されているため、Claude Code / Codex などのAIツールが文脈を理解しやすく、コード生成の精度が高まりました。
  • テスト容易性:
    • ドメイン層が外部に依存しないため、ビジネスロジックの単体テストが容易になりました。
 
最初からこの形で決まったわけではありません。
コンテキストを階層的に表現している時期もあったり、コンテキストを分け方や表現は適宜議論していました。linter での制御のしやすさやAI が理解しやすい構成を試行錯誤しつつバランスを見て決めていきました。
 

AI のルール体系の設計

プロジェクトのディレクトリ構造に沿って、階層的なルールファイルを作成しました。
今回のプロジェクトはAIの進化をキャッチアップしながら常にアップデートしていったので、アーキテクチャ含め、「どうAI を使うか?」を毎週・毎日議論し以下の形に落ち着きつつあります。
(AI の進化に合わせて今も日々アップデート中)
 
.agent-rules/ ├── global.md # プロジェクト全体のルール ├── global-monorepo.md # モノレポ構造のルール ├── global-database.md # データベース命名規則 ├── git.md # Git操作ルール ├── apps/ │ ├── apps.md # アプリケーション層の共通ルール │ └── partner-hub-api/ │ └── ... # アプリ固有のルール └── packages/ ├── packages.md # パッケージ共通ルール ├── package-domain-layer.md # Domain層の詳細ルール ├── package-application-layer.md ├── package-infrastructure-layer.md ├── package-public-layer.md └── partner-hub/ └── ... # パッケージ固有のルール
 

重要なポイント

AIエージェントが自動的に以下のベストプラクティスを適用しています
  1. 適切なレイヤー分離
      • Domainは純粋なビジネスロジック
      • Infrastructureはデータアクセスのみ
      • Publicは外部向けFacade
  1. 命名規則の遵守
  1. デザインパターンの適用
      • Immutable Entity with factory methods
      • Repository pattern
      • Dependency Injection with decorators
  1. バリデーション戦略
      • Domainレベルでのzod validation
      • UseCaseでのビジネスルールチェック
 

定性的な効果

  1. 一貫性の向上
      • 全メンバーが同じパターンで実装
      • コードレビューでの議論がビジネスロジックに集中
  1. 学習曲線の緩和
      • AIエージェントとの対話で実践的に学習
      • ドキュメントを読むより早い理解
  1. レビュアーの負担軽減
      • 基本的なアーキテクチャ指導が不要
      • より高度な設計判断に時間を使える
 

アーキテクチャのモダナイズだけでは解決できなかった課題

クリーンアーキテクチャとDDDの導入により、コードの保守性と拡張性は大きく向上しました。しかし、アーキテクチャの改善だけでは、プロジェクトの遅延をリカバリするには至らず。ここでは、実際に直面した3つの課題と、そこから得た学びを共有します。

1. 実装しながらのルール整備とAIの学習

プロジェクト初期は、アーキテクチャのルールを実装しながら整備していく状況でした。そのため、AIが過去の実装パターンを参考にコードを生成すると、最新のルールに準拠していないコードが生成されることがありました。
これにより、レビュー段階での修正や人手のコーディングが多く必要となり、想定以上の時間を要しました。
 
この経験から、AIを効果的に活用するには、ある程度ルールが確立された状態が望ましいという学びを得ました。とはいえスピードを優先していく中でゼロイチの開発でルール整備にどれだけ時間をかけるかは難しい問題です…これらは継続して議論していく必要があると感じています。
 

2. ドメイン知識の不足と誤ったコンテキストへの実装

AIに十分なドメイン知識を与えられていなかったため、期待するコンテキストとは異なるコンテキストに機能を実装してしまうケースがあり、境界付けられたコンテキストの意図が正しく伝わらない場面がありました。
この課題から、AIへのコンテキスト提供の重要性を学びました。現在も整備中ですが、AIが各コンテキストの目的を理解し適切な場所に実装できるよう改善しています。
 

3. テスト戦略の認識合わせ不足

プロジェクト初期は、どのレイヤー(Domain/Application/Infrastructure)にどこまでのテストを実装するか、チーム内での方針が明確ではありませんでした。
その結果、レビュー時に「このテストは必要か」「別のレイヤーでテストすべきではないか」といった議論が発生し、レビューサイクルが長期化しました。
 
この経験を踏まえ、まずはスピード優先で、プレゼンテーションレイヤーに一気通貫のテストを実装する方針とし、実装が一通り揃ったタイミングで各層のテストの指針を設けていくことで、レビューがよりスムーズになりました。
 

学びと改善

これらの課題は、アーキテクチャの技術的な優位性だけでは解決できないものでした。技術的な基盤整備に加えて、チーム内のルール整備、ドメイン知識の共有、テスト方針の明文化が不可欠であることを学びました。
 
現在では、これらの学びを活かし、5章で述べるような「AIとの協業」をより効果的に進められる体制を構築しています。遅延という苦い経験は、より堅牢な開発プロセスを確立するための貴重な教訓となりました。
 

4. Deep Dive: 拡張可能な「サイトコントローラー統合」設計 🤿

今回最大の技術的難所であった「複数のサイトコントローラーとの連携」について、Adapterパターンを用いた解決策を紹介します。
 

課題:プロバイダーごとの仕様差分

  • A社: SOAP/XMLベース。一部機能のみ提供。
  • B社: REST/JSONベース。全機能提供。独自の日付フォーマット。
 
これらをif文で分岐させて実装すると、コードはスパゲッティ化し、保守不能になります。
 

解決策:Port and Adapter パターン

notion image
ドメイン層に統一インターフェース(Port)を定義し、各プロバイダーの実装(Adapter)がそれを満たす形にしました。
 

Step 1: 統一インターフェース(Port)の定義

プロバイダーの違いを吸収する、我々のドメインにとって理想的なインターフェースを定義します。
// domain/ChannelManagerPort.ts export interface ChannelManagerPort { // 在庫状況の取得(入力・出力は共通のDTO) getRoomTypeAvailabilities( input: GetRoomTypeAvailabilitiesInput ): Promise<ChannelManagerAvailability[]>; // 予約作成 createBooking(input: CreateBookingInput): Promise<CreateBookingOutput>; // ... }
 

Step 2: Adapterの実装

各プロバイダー固有の処理(XMLパースや認証、メンテナンスエラーのハンドリングなど)は、Adapter層に閉じ込めます。
 
パターンA:部分的実装
// infrastructure/a/AChannelManagerAdapter.ts @AppService() export class AChannelManagerAdapter implements ChannelManagerPort { constructor(private readonly service: TL_LincolnService) {} async getRoomTypeAvailabilities(input: GetRoomTypeAvailabilitiesInput) { // プロバイダー固有のサービスへ委譲 return this.service.getAllAvailabilities({ ... }); } // 未実装機能(未サポート機能は明示的にエラーを投げます。) async getRoomTypes(_input: GetRoomTypesInput): Promise<never> { throw new NotImplementedError("A does not support getRoomTypes"); } }
 
パターンB:完全実装&クライアント分割
// infrastructure/b/BChannelManagerAdapter.ts @AppService() export class BChannelManagerAdapter implements ChannelManagerPort { constructor() { // 機能ごとにクライアントを分割し、Adapterは統括するだけ this.availability = new BClientAvailability(this.client); this.booking = new BClientBooking(this.client); } async getRoomTypeAvailabilities(input: GetRoomTypeAvailabilitiesInput) { // 日付の範囲制限などのAPI制約対応は ClientAvailability クラスの中に隠蔽 return this.availability.getAvailability( ... ); } }
 

Step 3: DIによる切り替え

アプリケーション層(ユースケース)は ChannelManagerPort だけを知っていればよく、実行時に適切な Adapter が注入されます。
これにより、新しいプロバイダーを追加する際は、新しいAdapterクラスを1つ追加してDIコンテナに登録するだけで済み、既存コードを一切汚しません。
 

5. まとめと今後の展望 ⭐

学びの総括

今年の『NEWT Partner Hub』開発は、「DDD × AI 」 がうまく噛み合ったプロジェクトでした。
外部 APIの複雑さに苦しみ、見積もりの甘さで遅延するなどという失敗もたくさんありましたが、最終的には、”堅牢で、運用しやすく、拡張しやすいプロダクト” が形になりました。
 
そして何より、AI が “チームの一員” として開発速度と理解速度を大きく押し上げてくれたというのが、2025 年を象徴する経験です。
 

ネクストアクション

今後は、今回のようなかなり巨大なプロジェクトはあまりないですが、体制がかなりスリムになり、実装担当が3名となっているので、より役割の垣根を超えた振る舞いが求められます。
 
現在も以下の実用化を目指し、AI 活用フルベットで取り組んでいます。
💡
  • issueの起票やissue のPRの自動化
  • AI と協力した見積もり・スケジュール・リスク管理
  • 問い合わせ対応のAI 活用
 
AI と協業しチーム全員がリーダーとなってプロジェクトを推進できる体制・基盤づくりを引き続き推進していきます!
 
 

📣 12月のイベント開催のお知らせ

令和トラベルでは、毎月技術的な知識や知見・成果を共有するLT会を毎月実施しています。発表テーマや令和トラベルに興味をお持ちいただいた方は、誰でも気軽に参加いただけます。

【12/4開催!】2025年総決算!エンジニアリングマネージャーお悩み相談室 LIVE

2025年最後のイベントとなる「NEWT Tech Talk Vol.19」は エンジニアリングマネージャーお悩み相談室 を開催いたします!当日は、株式会社カケハシ・株式会社スマートバンク・株式会社LayerXを交えパネルディスカッションを開催。
2025年7月7日に発売された書籍『エンジニアリングマネージャーお悩み相談室』の著者と、書籍レビューに参加したEM実践者たちが集い、EMたちのさまざまな課題や悩み、またそれに対する解やアプローチについてここ限りのオフレコトークを語り合います!
そのほか、毎月開催している技術発信イベントについては、connpass にてメンバー登録して最新情報をお見逃しなく!
 

【NEWT Chat リリース記念】AI × Travel Innovation Week 開催!

12月3日より、「NEWT Chat」誕生の裏側や開発ストーリーをお届けする特別企画 “AI × Travel Innovation Week” を令和トラベルのnote上で開催中です!
「NEWT Chat」のリリース背景、プロダクトの価値、開発体制、そして今後の展望など、新規事業の “舞台裏” を12月3日〜6日まで毎日公開予定です。特に、AIプロダクト開発に関わるエンジニア・PMの皆さまにとって学びの多い内容となりますので、ぜひご覧ください。
▼ AI × Travel Innovation Week のnoteはこちら:
※上記リンクは12/3 午後にページを公開いたします
 
旅行・観光業に特化したAIエージェントチャット「NEWT Chat(ニュートチャット)」についてはこちらから。
 
 

冬の特大セールも開催中!

NEWTでは現在、海外旅行やホテルをおトクにご予約いただける『冬のごほうびセール🎁 BLACK FRIDAY』が11/27〜スタートしています!ぜひこの機会にご利用ください!
 

令和トラベルでは一緒に働く仲間を募集しています

この記事を読んで会社やプロダクトについて興味を持ってくれた方は、ぜひご連絡お待ちしています!お気軽にお問い合わせください!
フランクに話だけでも聞きたいという方は、カジュアル面談も実施できますので、お気軽にお声がけください。
 

📣宣伝

次回のNEWT Product Advent Calendar 2025Day4は、「Scaling Generative AI Evaluation at Reiwa Travel: LangSmith for AI Observability」と題してAX室のlukasが担当します。次のブログもお楽しみに!
 

# advent calendar

# backend

# AI