ドメイン駆動設計の基礎知識

増田亨氏(以下、増田):こんばんは。ギルドワークスの増田です。

実は、テクロスさんとは『UNITIA』の開発初期に京都に隔週ぐらいで何度かお邪魔して、開発チームのみなさんと設計を議論したりしました。そういった縁があって、今日は講師として招かれました。ありがとうございます。

「ドメイン駆動設計の実践」ということで。私の専門はいわゆる業務アプリケーションです。ただ、個人的にはゲームは好きなので。ドラクエは初期バージョンの頃は会社を2週間ぐらい休んでやったりとか、そういう人生を送っていたので思い入れはけっこうあります(笑)。

仕事としては業務系アプリが多くて。ドメイン駆動設計ということで、いろいろ自分の仕事で設計のやり方や考え方を勉強するなかで、「こういうふうにしたら、こうなるね」というような、そういう情報をエンジニアの仲間と共有しながら、また次の新しいものを作っていきたいなと思っています。

自己紹介として、私が世間様から見られるようなところにアウトプットできているものとして、本を……今日も出させていただいております。

SlideShareに、ドメイン駆動設計の設計ネタ、あるいはオブジェクト指向という観点でいろいろ出させていただいているので。

最近はあまり書いていないのですが、「システム設計日記」というブログで、いろいろドメインの設計ネタを書いていたり。あとは、GitHubにシステム設計ということで、アカウントでドメイン駆動設計の参考コードのようなものを出したりしています。

本日の内容としては、「ドメイン駆動設計の基礎知識」ということで、基本的には「ドメイン駆動設計本を読んでいない」「ちょっとネットで出てきた情報に触れたことがある」というぐらいの知識でも理解していただけないかなということをターゲットにしました。

逆に言うと、「ドメイン駆動設計をけっこう勉強したよ」という方や、ドメイン駆動設計に出てくるいろいろな用語をご存じの方は「えっ?」と思うような内容です。いわゆる「パターン名」のようなものはぜんぜん出てきません

このあと、ミートアップでみなさんとお話しする機会はご用意いただけているということなので、もしなにかあれば、そこでまたそういう話も一緒にしたいと考えています。

「ドメイン駆動設計はこういう考え方でこうやる」というところを全体で話しながら、その中にゲーム開発への活用の可能性。それから、ゲーム開発に取り入れるときに「これは相当ハードルが高いな」と思うことがいくつかあって。

そのあたりについても、一応ハードルはハードルということで共有させていただいて。「では、そのハードルに対してどうするのか」ということ……「私だったらこうするかな」というあたりの話をさせていただきます。だから、これはヒントなんですね。答えではない。さすがに答えは見つけられていません。

ドメイン駆動設計とはなにか?

では、ドメイン駆動設計の基礎知識ということで「ドメイン駆動設計とはなにか?」という話です。まず、ものすごく単純な話、ドメイン駆動設計はソフトウェア設計の考え方とやり方の1つなんですね。世の中には、いろんな設計のスタイルや考え方の方法論があって、ドメイン駆動設計は、そういう設計スタイルの1つです。

まず「ソフトウェア設計をどうするんだ?」という議論が大前提としてあって、その中で「こういうことを重視する」「こういうことはいったんあまり触れないようにしよう」というような、設計のメリハリのつけ方、設計のやり方といった話だと理解すると、このドメイン駆動設計というものがある程度つかみやすくなるのではないかなと考えています。

ソフトウェア設計という考え方で、「基本の原則」や「設計というものはこうだよね」という議論はあるんですけれども、ソフトウェア設計を私なりにものすごく単純化するとしたら2つです。

1つは関心の分離。みなさんもよくご存じのように、ソフトウェアを作るときには、本当にいろいろ有象無象なことを解決しながらコードにしてソリューションとして作り上げていかなければいけない。そのときに、なんでもかんでも関心を持って、手当たり次第に手を出してもうまくいかない、というのはよくわかっていることなんですよね。

だから、そのときに、どちらにしてもさまざまな関心事・心配事を片付けていかなければいけないなら、心配事・関心事をどういう単位に分けて考えたほうがアプローチしやすいだろうと。そういう考え方がそもそもの関心の分離。

そういう意味では、関心の分離とはソフトウェア設計よりももっと幅広い。なにか問題が起きたときにどうしようかと言ったら、なにか1個の複雑な問題としてではなく、いくつか問題を分割で考えていって、1つずつ潰していけばいいよね、という、いろんな問題解決の考え方の一般原則です。

それをソフトウェアで実現するという文脈で考えたら、出てくるキーワードは「モジュール構造」。つまり、ソフトウェアで関心事が分離できているということは、モジュール単位に関心事が分かれていて、関心事と関心事の関係がモジュール間の関係として具体的にコードで書かれている。

だから、「関心事を分離する」というのは、いわゆる考え方的なことについて、「具体的にソフトウェア開発にどう活かすのか」と言ったら、その関心事をソフトウェアの塊の単位、モジュール単位にどう分けるか、モジュールをどう組み立てるかという議論になるだろうと。

そういう意味で、ソフトウェアの考え方や方法論・手法、あるいはデザインパターンのようなものはどういう分離の仕方があって、それを具体的にソフトウェアの部品単位、モジュール単位に分けるにはどんなやり方があるのかの参考情報なんですね。

もちろんいろいろなやり方があるので、設計のAというスタイルと、Bというスタイルと、Cというスタイルでは、「どこを選択するか」「選択の余地があったときにどちらを重視するか」ということが、当然違ってきます。

だから、ドメイン駆動設計を理解するということを別の言い方をすると、ドメイン駆動設計において関心というのは、どう捉えて分離すればいいと考えているのか。その考えた関心の分離は、ソフトウェアのコードとしてどういうモジュール単位で分割することにこだわって設計することを重視しているのか。そういう目でドメイン駆動設計を理解しようとすると、特徴が見えてきます。

特徴が見えてくるということと、それがサクッとできるようになることは別ですけれども、ドメイン駆動設計の特徴を、私がどうとらえているかをお話ししたいと思います。

ドメイン駆動設計における関心の分離と、それをソフトウェアとして具体的に記述するには、こういうことにこだわってモジュールを組み立てようとしている。そういうことをお話させていただきます。

ドメインロジックに焦点を合わせる

ドメイン駆動設計の特徴は、この3つです。

ソフトウェアのさまざまな関心事の中でドメインロジックに焦点を合わせる。これが一番のこだわりポイントです。

これは、このあといろいろ具体的に説明すると「え?」と思うようなことがあるかもしれませんが、まず1つ覚えてほしいのは、ソフトウェアを作るときにさまざまな関心事があるなかで、ドメインロジックに焦点を合わせる。

だから、こうやって写真を撮るときにどこにフォーカスを合わせにいくかと言うと、ソフトウェア全体・ゲーム全体ではなくて、その中のドメインロジックという場所に焦点を合わせるということに非常にこだわっている。

「ソフトウェアは全部作らなければダメですよね」という議論を始めてしまうと、それはドメイン駆動設計がやろうとしているスタイルではありません。とにかくドメインロジックに焦点を合わせることにこだわる。

逆に言えば、焦点を合わせることは、焦点から外れているものに関しては、少なくともある瞬間においてはほとんど見えなかったり、ぼやけたりしている。そこはむしろぼやけていてもよくて、ドメインロジックというところについて、くっきり焦点を合わせて写し撮るんだということがドメイン駆動設計のこだわりポイントです。これが設計スタイルとして1つ非常に特徴があります。

オブジェクト指向でモジュール化する

それから、ドメイン駆動設計では、関心の分離やドメインロジックが焦点で、どうやってモジュール化するかといったときに、オブジェクト指向でモジュール化するとことを非常に重視します。

この「オブジェクト指向」という言葉自体が非常にわかりにくいので、オブジェクト指向のモジュール化とはどういうことなのか。あるいはオブジェクト指向ではないモジュール化とはどういうモジュール化なのか、ということを今日はお話しさせていただきます。

まず、言葉として明確に覚えておいていただきたいのは、オブジェクト指向でモジュール化するということに非常にこだわっている。少なくともエヴァンス本はそういう文脈の中でドメイン駆動設計というものを語っていると。

それから、「インクリメントに設計する」というのは、実はスタイルというよりも必然です。ドメインロジックに焦点を合わせてオブジェクト指向でモジュール化しようとすると、アップフロントで良い設計がバチっと見つからないんですね。

ドメインロジックに焦点を合わせるのはいいけれども、どうやって焦点を整理していこうか、ドメインロジックをどうやってモジュール化していこうかといったときに、相当試行錯誤が入ってきてしまう。

だから、それは「アップフロントできれいに全部設計して、紙の上できちんとモジュール化が終わったらコードに落とそう」というのではなくて、「コードに落としてモジュール化の実験をしながら、だんだんよりよいモジュール構造を見つけていこう」というアプローチ。

これはなんというか、スタイルの選択というより、ドメインロジックに焦点を合わせて、一生懸命オブジェクト指向でモジュール化しようとしたら、とてもアップフロントにはできませんよ。インクリメントにやっていくしかないよ、という話です。

関心事の違い

ではこの3点について、まずは1番目の「ドメインロジックに焦点を合わせるというのはどういうことなんですか?」というところからご説明します。

「従来の設計」というバクっとしたまとめ方をさせていただきましたけれども、基本的にはみなさんが経験してきたプログラミング、あるいはソフトウェア設計を覚えて実践されているのは、簡単に言えば従来の設計が大半です。本やネットにあふれている情報も、サンプルコードも、ほとんど従来の設計です。

ドメイン駆動設計はそうではないんだよという話です。まず、最初のドメインロジックに焦点を合わせるということに対して、従来の設計はどこに焦点を合わせるかと言うと、入出力なんですね。

私どもが最初にソフトウェアのことを勉強したときには、入力と出力があって、間に処理があるのがソフトウェア、プログラミングだと教わりました。だから、「まず入力・出力を固めて、定義してから処理を書きなさい。所詮プログラムは入力と出力の変換なんだから」と教わったんですね。だから、それが従来の設計の入出力に焦点を合わせるという非常に基本のスタイルになります。

それに対して、今日はゲームの話も織り交ぜながら、できるだけ具体的に(ドメイン駆動設計は)入出力ではないんだと。ロジックに焦点を合わせるというのは入出力に焦点を合わせる話とはちょっと違うんだよということが1つのメインテーマです。

入出力に焦点を合わせると、ユーザーインターフェースのことを考えて画面を定義して、データベースを設計して、通信処理をするという、非常にストレートに関心事が出てきますよね。

それに対して、ドメインロジックに焦点を合わせたときに関心事は何になるかと言うと、まずドメインの状況を表している値の種類。所詮コンピュータなので、内部では、最後は数値化して演算するというふうになっているはずなんですね。

だから、ドメインロジックに焦点を合わせるというのは、要するに値の種類をまず見つけて、それに対してどんな計算ロジック、判定ロジックがあるかということを見つけ出して、その判定結果や計算結果をどう表現するんだと(いうことです)。

つまり、入出力はぜんぜん関係ないんですね。メモリ上でどういう値をどう表現して、メモリ上でその値をどう計算、演算かけて、メモリ上でそれをどういうふうに結果、値として表現するのかということに焦点を合わせて、入出力に関してはぼやーっとしたままでいいよと。

「いいよ」というのはアプリケーション全体の開発という意味ではなく、設計のアプローチとしてですね。まずはロジックに焦点を合わせることによって、いったんは入出力のようなものは、周りにある焦点になっていない背景としてぼやーっと見ておけばいいよ、というのがドメイン駆動設計の基本的な考え方。

私自身もそうだったんですけれども、このあたりは従来の設計に慣れていれば慣れているほど、従来の設計できちんと結果が出せる人であればあるほど、ある意味、違和感があるスタイルなんですね。逆にその違和感を感じていただけたほうが、ドメイン駆動設計はどういうものなのかというのはある意味わかりやすいと思うんですよね。

言葉を聞いて「そうか。わかった」というレベルで「わかった」と言われてしまうと「それはちょっと違うのでは?」と思ってしまいます。

ドメインロジックの構成要素

さっき言ったように「値に焦点を合わせる」「値の計算に焦点を合わせる」というのをもうちょっと具体的に言うと、ゲーム分野に行くまえに、一応、業務アプリケーション分野から行きましょうか。

ドメインロジックは、業務アプリケーションでは、「ビジネスロジック」「ビジネス部分」と言われるものです。ビジネスを構成する概念としては、顧客、商品、販売、出荷、請求といったものに対してビジネスルールがありますというように、ビジネスは俯瞰できます。

それに対して、実際にビジネスについて、どういう状況で、どういう計算・判断をするかと言うと、対象になっている値を少し例で挙げると、金額、数量、日付、日数計算といった値を扱いますよね。

あとは、金額=単価×数量というような単純な計算だったらExcelでもパパっとできますが、プログラムを書いてがんばっていろいろ苦労しなければいけないのは、会員区分、商品区分、取引区分といった、「区分の組み合わせによって計算方法を変えてくれ」「状態がこういう場合のときだけはこうしてくれ」というように、場合分けがいっぱい出てきてしまうときです。そこの複雑さをどういうふうにしていこうかというのが業務アプリケーションでは肝になっていきます。

実際、ビジネスアプリケーションの場合、個々の計算自体はそんなに難しいことをしないですよね。業務アプリケーションでアルゴリズムをやるという時代はもうなくなった。

大昔、私が始めた頃はソートロジックを自分で書かなければいけなかったので、業務アプリケーションでもアルゴリズムなども、勉強しなければいけなかったんですけれども、いまどき、ソートロジックをアプリケーションの中に書くなんてマニアックなことをやっていたら怒られますよね。

ただ、さっきも言ったように、実際には業務のアプリケーションはすごく複雑です。「なんで複雑になってしまうのか?」と言ったら、扱わなければいけない値の種類はけっこう多いからです。やっぱり区分系ですよね。

「こういう場合にはこうしてくれ。こういう場合とこういう場合のどちらかの場合にはこうしてくれ。だけど、こういう場合だけはこうしないと」というようなことを全部洗い出して整理して、きちんと辻褄が合って整合性が取れた状態で動かすようにするというのはかなりハードルが高いです。

入出力ではなく、値の種類とルールに注目する

ドメインは「名前空間」という捉え方でもいいかもしれませんが、「問題領域」「対象領域」という言葉で、ある領域、ある種の空間です。ある種の空間であるドメインに対して、業務アプリケーションという空間ではなく、ゲームという領域で今のようなドメインロジックの構成要素を考えるとどういうことになるでしょう。

一番上のビジネスを構成する概念はゲームを構成する概念では、具体的に用語としてどんな用語が出てくるかというと、最初に、「ゲームの世界観」「キャラクターの設定」「シナリオ」という中に「ゲームの世界」というものを表す言葉がいろいろ出てきますよね。まずそれが背景というか、まさに世界観になっていますね。

ソフトウェア開発という観点からすると、ゲームの状況を表現する値にはいろいろありますよね。RPG系だったら「レベル」「ゴールド」「HP」「MP」「SP」「経験値」。こういうものが当たり前のように出てきて。それから、アイテムの種類、武器の種類、防具の種類、モンスターのようなものに関してもいろいろな種類があるかもしれない。

そういう、いろいろな値が全部組み合わさってゲームのロジックができあがっています。

それから、そういう値に対して、例えばバトル開始条件の判定のときのロジック。だいたいこのあたりがいろいろ組み合わさったかたちになると思います。あとはバトルをしたときのダメージやポイントの計算ルール。それから1つのバトルが終わるときの勝敗の判定ルールといった、いろいろな計算ロジック判定ロジックがあるわけですね。

ドメイン駆動設計は、こういうところにフォーカスを当てます。入出力ではなく、どんな値を扱わなければいけなくて、その値はどういうふうに計算・判定するのか、ルールはなにがあるか。そういうものをバーっと集めて整理して1つのモジュールにする。1つと言うか、複数のモジュールにして組み合わせるという発想なんですね。

業務アプリケーションの場合は、画面の入出力、データベース、APIがいわゆるドメインロジック以外の入出力の関心事です。ゲーム分野でドメイン駆動設計をやろうとした場合、かなり大きなハードルになるのは、今のゲームはビジュアルデザイン、サウンドエフェクト、インタラクションで差別化しているので、がんばらなければ、ゲームとして競争力がないわけですよね。つまり入出力は重要な関心事なわけです。しかし、入出力に焦点を合わせてしまうのは、ドメイン駆動設計のアプローチではない。

ドメインロジックに「従属すべき」関心事

だから、入出力を否定するわけではないですが、アプリケーション開発で必須の要件として、まず1つ大きな話として、ドメインロジックとはさっきの計算、値の計算ロジック、ゲームルールのロジックそのものと、「このあたりの話は別の関心事だよね」「別の関心事として分けようよ」というのがドメイン駆動設計の基本的なスタンス。

それから、ちょっと言葉としては難しいですけれども、「ドメイン駆動設計をやるというのはこういうことか」というのがわかる瞬間の1つがたぶんここなんですね。

こういうものはドメインロジックに従属する要素なんです。だから、ドメインロジックがうまく分割して整理してモジュール構造ができあがってくると、そのドメインロジックのモジュール構造に対して周りにこういうものがモジュールとしてぶら下がってくる。

だから、全体としてはドメインロジックがしっかりしたストラクチャ、構造でモジュール化されたものが最初にできあがってきて、その周りに入出力に関するようなモジュールがぶら下がるようなかたちで全体ができあがっている。ゲームの世界は、ゲームのルールを扱うロジックがど真ん中にあって、そこのモジュール構造の周りに入出力のモジュールがぶら下がるという構造で作っている。

だから、作る順番もビジュアルデザイン、サウンドエフェクト、インタラクションデザインが先ではなくて、ゲームの世界観、キャラクター設定、シナリオについて、こんな感じでいこうと決まってきたら、次の段階でやるのは、ゲームのルールをもっと細かく作っていって、ゲームのルールを中心にまず構造設計をして。

こういうもの(ユーザインタフェース系)はもちろん並行して作っておいて、あとからプログラムとしてくっついてくる。全体のゲームの開発プロセスとしてこういうのを後回しにするというのではないんですよ。

ソフトウェアの開発チームから見た場合の順番、重点、作り方ということで言うと、ドメインロジック、ゲームのルールの計算ロジック判定ロジックが先にあって、そこを整理していって、だんだんそれができあがってきたら、こういうものをぽこぽこモジュールとしてプラグインしていくという、そういうイメージがドメイン駆動設計の作り方なんですね。

なぜそういう作り方をするかと言うと、これがドメイン駆動設計の理解の1つのポイントです。まず今日は「言葉としてはこういふうに考えています」「こういう考えなんです」ということを説明します。

これは私自身がドメイン駆動設計というものをずっと追いかけてやっているから、こういうことに対して実感があるということなんですね。逆に言うと、言葉だけを見て「わかった。ドメインロジックを中心でやろう」というふうにやっても上滑りするだけなので。

ドメイン駆動設計でやっていくというのは、このあたりの理由が腹落ちしてくるにつれてだんだんドメイン駆動設計をもっとやろうという話になるし、これが腹落ちしないかぎりはドメイン駆動設計でやるという言葉の掛け声だけをかけてもたぶん失敗するというか、うまくいかない。

ドメインロジックに焦点を合わせる理由

簡単にというか、あまり簡単ではないですが、もうちょっとシンプルに整理したかったのに結局こうなってしまいました。

まず、ソフトウェアの複雑さの原因はドメインロジックにあるんです。ゲームの場合は相当凝ったユーザーインターフェースを作っていらっしゃるでしょうから、このあたりも感覚的には「え、そうなのか?」と思うほうが自然なのかもしれません。

ドメイン駆動設計の基本的なスタンスは「ドメインロジックが複雑だからソフトウェアが複雑になるんだよね」「どんな値を扱わないといけない、値の計算がどんな計算しなければいけないか、それから区分の組み合わせ方が複雑になって計算が複雑になるから、ソフトウェアが複雑で面倒くさくなるんだよね」というのがドメイン駆動設計の基本的なスタンス、立ち位置です。

それから、原因がドメインロジックにあるんだということを前提にすれば、まずドメインロジックと入出力が混在していると、ただでさえグチャッと複雑なドメインロジックを見つけにくいので、入出力を分離してしまうことによってドメインロジックを扱いやすくしましょう。

ここで入出力の関心事を分離したドメインロジックだけを対象にすれば、少なくとも入出力と密結合になってしまっている中のドメインロジックと格闘するよりは、入出力をいったん置いておいて、ドメインロジックだけにフォーカスして整理できるので、3番目としては、ドメインロジックの輪郭や、さっきから出ているモジュール構造のようなものがだいぶ見えやすくなってくる。

4番目として、そのドメインロジックの輪郭や構造が見えてくると、今度は計算ロジックや判定ロジックについてぼやっとしていたものが、けっこう細かい話として見つかってきて、ソフトウェア開発をしているときにわりと早い段階で仕様が決まってくる。

全体の仕様としては7割ぐらいしか固まっていなくても、けっこう複雑なところのロジックはある程度開発ができるようになるということは非常によくあるので、ゲームもたぶんそうだと思うんですよね。

ゲーム全体のロジックとしてはまだ決まっていなくても、バトルや強さに関しては「今回はこういう扱いをしてみないか」というようなコンセプトが出てくれば、そこのモジュールだけを先行して作ってみる、試作してみる、というようなことはいろいろできる可能性があると思うんですね。

ロジックが整理されると入出力の記述もシンプルに

そうやってロジックの整理がやりやすくなってくると、5番目としては、計算ロジックと判定ロジックのほうが、入出力の記述がシンプルになります。

おそらくこの5番目が具体的に実感として持てるようになると、ドメイン駆動設計にはまってくるというか、「このドメインロジックを先行して整理すればいいんだ」となってくる。

たぶんここは大きな分かれ目ですね。ドメインロジックを入出力から抜き出してしまうことによって、計算といった細かい判定はすべてドメインロジック側のモジュールでやってくれるので、入出力は非常にシンプルな流れになるんですね。

「イベントに対してこうしてこうしてこうして」の「こうしてこうして」の中の判定や分岐というものを全部ドメインロジックのモジュールに委譲できてしまうので、イベントに対してなにをしなければいけないかというのは、あまりぐちゃぐちゃロジックを考えなくても、ドメインロジックができあがっているから、「バトル、ターンが変わった」「バトル終わったのか」というイベントさえ教えてあげれば、サクッと書ける。

ドメインロジックを入出力から分離することによって、ドメインロジックそのものも整理しやすくなります。入出力に関するコードも単純になります。ということは、両方を合わせたら、ソフトウェア全体が非常にわかりやすくなって、扱いやすくなる。見通しがよくなり、変更が楽で安全になる。

逆に言うと、見通しが悪い状況、入出力とドメインロジックがあっちこっちに複雑に絡み合って、どこになにが書いてあるかが非常にわかりにくい、あるいはどこに書いてあるかは特定できても、それを変えたときになにがどこに影響するのかがわかりにくいという状況にならないソフトウェアの構造を作り上げていこうというのがドメイン駆動設計の基本的なアプローチです。

ドメインロジックと入出力を分離する

ドメインロジックの関心事をソフトウェアの構造として入出力から明確に分離するというのは、ドメイン駆動設計の話が出てくると、よくアーキテクチャがドメイン駆動設計そのものであるかのような情報も多いんですけど、あながちそれは間違いじゃなくて。

トラディショナルな3層構造というか……プレゼンテーションに関するモジュール群、いわゆるロジックに関するようなアプリケーションのモジュール群、データベースアクセスや通信をするためのモジュール群という、関心事を3つに分けるということは、ある意味今は当たり前になってきています。

ドメイン駆動設計の場合、さらにこのアプリケーションを、「こっちは入出力処理やゲームルールをこういうときに実行してください」としたり、あるいはルールの実行結果をもらって「次はこうしようね」というコントロールだけをするようになって、基本的な計算や判定は全部外出しのモジュールにしてしまう。

3層+ドメインの構造では、ドメインロジックを実装したモジュールに大きく分けてしまう。ドメイン駆動設計の場合、このアーキテクチャ、ドメインロジックの分離が基本のアーキテクチャになります。

結局、さっきも言ったように、こういうところ(左の三層側)にいろいろな計算ロジックや判定ロジックがバラバラに置かれてしまうと見通しも悪くなるし、変更のときに非常にやりにくい。副作用が怖い。

それを、計算・判定ルールの記述をこういうかたちで独立のモジュールに抜き出して、ドメインのロジック、ゲームロジック、ゲームルールを計算・判定したいときにはこっち側がこのモジュールに依頼に行くというような構造を徹底しましょうということ。

ゲーム開発でのメリット

ゲーム開発でのメリットはもちろん、さっき言った理由そのままです。基本的には計算ロジックと判定ロジックの記述が整理できます。たぶん一番大きく顕著に表れてくるのは、同一ロジックの重複記述が防止できるということ。

同一ロジックというのは……ある程度プログラミングのスキルがある方たちは、例えばちょっとしたif文や判定ロジックを書いてもあまり気にならないですよね。いろいろなシーンで同じ判定ロジックを書いても、それは気にならない。

でも、それはすでにコードが重複していて、なにかそこに関連するルールを変えようしたときに、どこを直せばいいのかということを探し回らなければいけなくなる。たった1行の判定式のようなものでも、常にそういうリスクを背負っていますということです。

ドメイン駆動設計は、たった1行の判定式……例えば「必ずこの数値は正でなければいけない」というような正負の判定でも、1つのクラスにまとめてしまうことで、必ずその判定はそのクラスを使うようにしようということを徹底している。そうすることによって、「HPがいくつ以上だったら」「いくつ以下だったら」というロジックがあちこちに書かれるということをなくそうという発想なんですね。

それから、これはやってみなければ、感覚的にはわからないかもしれませんが……世界観、キャラクター設計、シナリオ構成は一応イメージやコンセプトづくりのようなかたちでやってあるので、それと非常に細かい計算ロジック、プログラミングレベルでの計算ロジックや判定ロジックがダイレクトに結びつかないかもしれません。

ドメイン駆動設計でドメインロジックだけをかき集めて、それをこうやってうまく整理していくと、それは結局、世界観やキャラクター設定の描いているコンセプトと、構造というか、枠組みと同期が取れてくるんですね。

逆に言うと、同期が取れてこなかったら考えなおしたほうがいい。例えば、ドメインロジック側では戦闘ロジックがすごくグワーッと膨れ上がってきているのに、世界観やコンセプトでは、戦闘よりも、その場その場でのおもしろさや意外性をがんばろうというようなコンセプトだったとしたら、それは明らかにゲームのコンセプト、ロジック、ソフトウェアの構造の重点がずれてしまっている。

意外性ということがコンセプトにあるんだったら、「意外性はどういう数値で表現できて、意外性を計算したり判定したりすることとはどういうことなんだ?」というようなパッケージというか、モジュールがバーっと膨れ上がってくるような構造になれば、意外性というコンセプトとソフトウェアの構造の同期が取れている。そういうことがあちこちで起きるはずなんですね。

そこを徹底すればするほど、「ゲームコンセプトをこうしよう」「このゲームに関しては、もっと次のステップとして、こういうバージョンでは、こういうゲームにしていこう」というような話がどんどん出てきたときに、もともとソフトウェアがそういう構造になっているから、「ここをこうしよう」「ここだけ拡張すればOKです」と(なる)。

そういうゲーム自体のコンセプト・内容の進化・成長・変化と、ソフトウェアとしての変化・成長との同期が取れるということがドメイン駆動設計が狙っているソフトウェアの作り方ですね。

それから、さっきも言ったように、やってみるとわかるんですが、入出力処理の記述が本当にシンプルになるんですよ。いかにちょっとした判定や分岐が入出力の記述を複雑にしていて苦しんできたかというのは、ドメイン駆動設計をある程度できるようになってからビフォー・アフターでコードを比べてみたら、間違いなく実感できる。

私も、この世界観とプログラムの内容が整合するというのは、どっちかというとたぶん相当やってから実感が出てきた話で、最初に出てきた実感はこれですよね。判定ロジックや計算ロジックというのだけをピュアに抜き出してしまった場合に、入出力の手順はこんなにシンプルになります。私がドメイン駆動設計を最初に取り組み始めた頃から加速がついてきた1つの契機になったというのは、入出力の記述が驚くほどシンプルになるという手ごたえからです。

ローグでロジックを理解する

それではデモということで、これは『ローグ』という、コンソールベースのゲームです。ご存じですかね。わざわざオールドファッションなコンソール端末で使っていた時代のものが今でもネットに公開されていて、私はこれでやっていました。

非常に単純な話で、今「h」を動かしていて、hで横に動いていくと、こうやって通路が見えてきて。また隣の部屋に入っていくと、星は「*」はゴールドですかね。

ここでなんか敵とぶつかったので敵と戦いにいって。私が戦っているのかな。ちょっと待って。ダメだな、負けそうだな(笑)。ああ、死んだ!(笑)。

(会場笑)

これ、簡単には復活できないんですよ(笑)。今のでも雰囲気は出ているか。でも、やっぱり画面を見たほうがいいね。

これね、セーブが途中からはできなくて、リスタートするたびにランダムに別のゲームが立ち上がるんですよ。だから、最後まで行きたかったら……このバージョンはセーブはできるのかな? セーブができるようになっているね。僕の時代にはなかった。

(会場笑)

ただ、僕がやっているときは、みんな朝から晩までUNIXの端末でこんなことをやっていたら怒られるので、バシっと消えてくれれば、またちょっと暇というかやる隙ができたら立ち上げたり。それから、夜中にやりだしたら夜が明けていたりして。

今、ヘルプ画面を出しました。

このシンプルなヘルプ画面が、ここで起こせるイベント一覧です。全部設計が終わっているんです。これが全イベントです。ここに出ているもの以外のイベントは起こせません。ここはキャラクタ端末型のいいところで、設計内容が丸見えなんです。

一応、ビジュアルと言うほどではないですが、私が最初に触ったときは実はこれ自体がなかった。カーソルで縦横に動くということができなくて、moveというコマンドを打つと今どうなっているというのがわかるという、本当に会話式になってたんですね。この画面で移動できるのは相当発展バージョン。

その代わりダンジョンっぽいというか。今でもダンジョンでまったく周りが見えないような状態にしますよね。そのモードしかなかった。なにかアクション……どっちかを向いてなにかイベントを起こすたびに、それに対して、無反応になったり、なにかが見つかったり、ドアがあったり、そういう感じで。

さっき言った計算や判定に使う値は、ここに見えているだけでも、「Level」「Gold」「HP」、それから「Str」というのは、強さですかね。それから「Arm」はなんだろう?

参加者1:Armor。

増田:「Armor」なのか。それから「経験」。内部的にもうちょっとなにか数値を持っているかもしれませんが、基本的にはこのくらい。常に状況を表す値の種類ということでは、7種類ですね。

モンスターの種類やアイテムの種類もアルファベットでしか表現できないので、数は限られているわけですね(笑)。アルファベットのAからGまで、Hまでというように、設計が非常に限定されています。

このゲームをやり始めて、話し始めると、終わらないんですよね。

ゲームロジックにフォーカスできる

今日はゲームの話ですが、結局、入出力から分離したゲームロジックだけというものにちょっと近いのがこれなんだという感じです。一応コンソールで入出力がついていますが、非常に貧弱な入出力しかないので、ほとんど裸のゲームロジックとインタラクティブにやる感じ。

今言ったように、ゲームの概念や状況を表現する値がほとんど大半を占めていて、裸で見えているような感じです。今の感覚で言ったらモデル部分のデバッガですよね。会話的にコンソールでデバッグしてみるというような。

この時代にも、ゲームがものすごく改良が進んでいるんですね。改良の方向として、さっき言ったように、キャラクターが縦横に動くようになったというのもけっこう革命的だったんですが、それがゲームのおもしろさを倍加するということはぜんぜんなくて。所詮あんな程度のユーザーインターフェースしかないから。

やっぱりなにがおもしろくなったかというと、ユーザーインターフェースが貧弱なので、もっとおもしろいゲームにするには、新しくバージョンが上がるたびに、ゲームロジック自体に改良が入るわけですよ。

「こういうときはこういう挙動をしたほうがいいよね」「こんなこともできるようになったほうがいいよね」ということ……いわゆるゲームの「どういうゲームがどういうふうに動けばもっと楽しくなるだろう」ということが、ネット上……と言っても今のような会話的なコミュニケーションはできなかったので、メーリングリストのようなもので流れていて。

私は開発には参加していませんが、メーリングリストをちゃんと登録していて。「次のローグはこんなのが出そうだぞ」「このローグはアメリカですごく人気があるみたいだから、ちょっと僕もダウンロードしてみよう」というように。ゲームのダウンロードでさえいろいろ大変な時代だったんですが。

いずれにしても、いわゆるロジックが関心事の焦点だということの1つのイメージとしては、このローグのようなコンソールレベルでのゲームというものに触ってみてもらうと、今の非常にリッチなユーザーインターフェースでゲームを作るというのとは違う感覚がご経験いただけるのかなと。

今日、こういうデモをさせていただいている理由は、ドメイン駆動設計の考え方として、最終的にはいろいろなユーザーインターフェースをくっつけるにしても、ロジックにフォーカスを当てたゲームの作り方・成長のさせ方というものがドメイン駆動設計的なアプローチなんですよ、という例(を紹介するため)なんですよね。

これで、ドメインロジックに焦点を合わせるという1番目の話がようやく終わりました(笑)。