宮古島
これぞ秘境の離島!「宮古島」に行くべき7つの理由 | RETRIP
時間があったので、行ってきた。
airbnbで宿を予約、LCCで格安航空券をと、リーズナブルに行けたし、 思ったより物理的な距離も精神的な距離も近かった。
1月はほぼほぼ雨かくもりぽいけど、気温は東京と比べて10度以上暖かいので最高。
「Team Geek」を読んだ
Team Geek ―Googleのギークたちはいかにしてチームを作るのか
- 作者: Brian W. Fitzpatrick,Ben Collins-Sussman,及川卓也,角征典
- 出版社/メーカー: オライリージャパン
- 発売日: 2013/07/20
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (20件) を見る
「良いチームを作るための指南書」、「他人とうまくやるためのHack」
geekに限らず、ソフトウェア開発に限らず参考になると思う。
TL;DL
- 良い・成功するソフトウェアは 「HRT = 謙虚 + 尊敬 + 信頼」の文化が根付いている
- ソフトウェアは人で成り立っている
- チームが目的を達成するためにHRTは必要不可欠なもの
- あらゆる人間関係の衝突はHRTの欠如によるもの
- ソフトウェアを書く理由は、ユーザ生活を豊かにするため
HRT
- Humility
- 世界の中心は君ではない。君は全知全能ではないし、絶対に正しいわけでもない。常に自分を改善していこう。
- Respect
- 一緒に働く人のことを心から思いやろう。相手を1人の人間として扱い、その能力や功績や高く評価しよう。
- Trust
- 自分以外の人は有能であり、正しいことをすると信じよう。そうすれば、仕事を任せることができる。
See also
武井壮 オトナの学校
ロジカルな考えの中にも熱があり、引き込まれた。
いい話だった。
何が価値を生むか
- 人が求める数
- 人・商品の価値はクオリティではなく、喜んでくれる人の数
2015年
2015年にあったことのまとめ
iOS
前半は業務でがっつりiOSを触っていた。
無事リリースも出来、その後、収益に直結する広告基盤の実装を終えた時には、
少し自信がついた。
Scala移植の
とか作って、ちょくちょくGitHubに草を生やしていた。
Android
Androidデビューを果たし、戸惑いながらもなんとか頑張っている。
Apple信者なので、Android端末自体そもそも触ったことがほとんどなかったが、
UI/UXともに何かと新鮮で楽しみながら開発出来ている。
この1年は"iOSを頑張る"と目標としていたが、
同時にAndroidにも触れることが出来たのは、貴重な経験で良かった。
output
前述のGitHubでのライブラリ作成など、前年までと比べて比較的コードを書いていた。
ただ、作って。はい終わりが多く、もっとコードベースの大きな、保守しなければならない何かを来年以降は作っていきたい。
(OSSへのPRも今年初めて行った。)
また、初Advent Calendarに参加した。
(去年Swift Advent Calendarに参加しようとしたら、一瞬で枠が埋まってしたまったので今年は参加できてよかった)
- SwiftでLens(すごいgetter/setter)を実装してみた - Qiita
- esa.io - esaのChrome Extensionを書いてみた - Qiita
- SwiftでArrayの要素を振り分けたい - Qiita
全部ギリギリに書き上げ、日付オーバーしたのもありますが‥
あ、あと、読書系の備忘録になっていますが、このはてなブログも今年始めた。
転職
会社は働きやすく、休日に旅行するぐらい仲が良く楽しかったが、この環境に甘えているな、
今とはまた違う気持ちで働いてみたいと考えて転職することにした。
12月末日が最終出社、1月は長めのお正月休みを頂き、2月から社会復帰します。
新しい環境に関しては、2月以降にブログにまとめようかなと思います。
引っ越し
杉並区 > 豊島区に引っ越した。
池袋は学生時代からよく行っているし、新鮮味はないのだが、歩いて帰れるのはとても良い!!
まとめ
今年は新しいことにチャレンジする事が出来、その中でoutputは出来た。
後半は引っ越し関連でバタバタして、積み本になってしまった本が大量にあるので来年は消化していきたい。
来年
"大量のinputを怠ることなく、前年より質の高いoutputを行う。> 勉強会で発表する"
"iOSを頑張る"
"Storeにアプリを出す"
「Application Architecture for .NET」を読んだ - 3部(後編)
.NETのエンタープライズアプリケーションアーキテクチャ 第2版 (マイクロソフト公式解説書)
- 作者: Dino Esposito,Andrea Saltarello,日本マイクロソフト(監訳),クイープ
- 出版社/メーカー: 日経BP社
- 発売日: 2015/06/04
- メディア: 単行本
- この商品を含むブログ (3件) を見る
「Application Architecture for .NET」を読んだ - 1部
「Application Architecture for .NET」を読んだ - 2部
「Application Architecture for .NET」を読んだ - 3部(前編)
の続き
第三部サポートアーキテクチャ(10, 11, 12, 13章)のメモ。
TL;DL
- CQRSはトップレベルのアーキテクチャではなく、テクノロジに依存しないアーキテクチャ
- ある程度設計パターンに依存するが、CQRSはパターンそのもの。CQRSはシンプルかつ強力で、一般的なアプリケーションに適している
- CQRSはドメイン層を切り離すことと、読み取り/書き込みに別々のモデルを使用することを提唱する
- 最大のポイントは、コマンドをクエリから引き離す
- 読み取り用(1つの中間層がデータを取得する)/書き込み用(1つの中間層がシステム状態を変更するコマンドを処理する)にストレージを用意することになる
- Eventは分析と実装に対してタスクベースのアプローチを推進する
- CQRSの真価は一方を最適化することでもう一方が機能しなくなるというリスクを負うことなく、コマンドとクエリのパイプラインを自由に最適化できる点にある
- Cutting Edge - 一般的なアプリケーション向けの CQRS
- Cutting Edge - CQRS とイベント: 強力なコンビ
10. CQRSの紹介
- DDDの当初のビジョンに不可欠なのは、境界付けられたコンテキストに対して推奨されるアーキテクチャです
コマンドとクエリの分離
- 一般的に言えばソフトウェアシステムで実行されるアクションはクエリかコマンドに分類できる
- クエリ: システムの状態をいかなる方法でも変更せず、データを返すだけ(読み取り)
- コマンド: システムの状態を変更する。ステータスコードか確認応答を返すとしてもそれ以外のデータは返さない(書き込み)
- CQRSではDomain層を1つではなく2つ使用する
- CQRSの利点
- 設計の単純化
- スケーラビリティ向上の可能性
クエリスタック
コマンドスタック
- コマンドスタックの関心はアプリケーションの状態を変更するタスクのパフォーマンスだけ。アプリケーション層は従来通り、プレゼンテーションからリクエストを受け取りそれらを実行する手はずを整える
- コマンドとはバックエンドに対して実行されるアクションのこと(単一方向)
- ex. タスクを実行したユーザがフィードバックを期待して待つケース
- コマンドとクエリが同じコンテキストに属するのであれば問題はない
- 別々なら、1つは対話形式で利用され、もう1つはプログラムから開始される
- 入力を処理するためのフロントエンドリクエストはアプリケーション層(受信者)に送信されるメッセージと見なせる
- メッセージはこの後の処理に必要なデータを含んだDTO
public class Message {
// 必要に応じて一般的なプロパティを定義
// 但し、クラスはプロパティを持たない単なるマーカーでも構わない
}
- メッセージはコマンドとイベントの2種類がある
- コマンドは命令型で明示的なリクエスト
- 1つのハンドラに送信される
- システムによって拒否されることがある
- 効果はシステムの状態に応じて異なることがある
- コマンドが境界付けられたコンテキストの境界を超えることは無い
- イベントは既に発生していることの通知としての役割を果たすメッセージ
- イベントを処理するハンドラはいくつ指定しても構わない
- イベントのサブスライバがそのイベントが発生した境界付けられたコンテキスト内にいるとは限らない
- 既に発生している何かを表す単純な理由によりImmutableとみなすべき
- コマンドは命令型で明示的なリクエスト
- イベントソーシングは、メッセージを保存することで詳細で包括的な監視ログを形成できること
- システム内で発生したイベントは後から確認出来る、イベントを再現、推定することが可能
- コマンドを非同期で実行する必要がある場合は、コマンドではなくイベントとして設計すべき
- コマンドとイベントはどちらもMessageの派生クラス
- イベントに関しては何かが発生したことを反映した名前にすること
- ex. Is-Gold => CustomerReachedGoldStatusEvent
// Command public class CheckoutCommand: Message { public string CartId { get; private set; } } // Event public class DomainEvent: Message { // 一般的なプロパティ // イベントが発生した時間を追跡するtimestamp // イベントを発生させたuserId // イベントをハンドラで処理できるかのversion } public class OrderCreatedEvent: DomainEvent { // イベントの名前はより詳細で明確に }
- コマンドはコマンドバスと呼ばれるプロセッサにより管理
- イベントはイベントバスと呼ばれるコンポーネントによって管理
- コマンドバスはメッセージ(コマンドを実行するリクエストとイベントの通知)を受け取り、それらを処理する方法を見つけ出す単一のclass
- コマンドバスは実際に処理を行うのではなく、処理できるハンドラを選択する
- コマンドと関連するイベントを処理するプロセスはSagaと呼ばれる
- Sagaコンポーネントは、論理的に関連するメソッドとイベントハンドラを集めたようなもの
- ドメイン層はコマンドスタックに配置され、はるかに単純なデータアクセス層はクエリスタックに配置される
- 現実のシステムの多くは読み取りと書き込みに単一のDBを使用する
- コマンドモデルとクエリモデルを別々に定義したとしても同じDBを共有することは可能(どうのようなシナリオに取り込むかによる)
イベントバス
- bus: データをやり取りするための伝送路
- コールバックメソッドを呼ぶタイミングでイベントを発火し、コールバックインタフェースの実装ではなく、イベントを常時監視するメソッドを用意しておいて、発火したタイミングで呼び出されるようにする仕組みとして、EventBus を使うことで、インタフェースによる依存関係を整理したり、コールバック地獄を解消したりすることができる
- EventBus という中間層を介したコミュニケーションを用いることで、ModelとControllerは直接結合することがなくなることで、Controllerが実装したコールバックインタフェースの管理をしなくても良くなる
11. CQRSの実装
クエリスタックの実装
- クエリスタックのドメイン層がほとんど空でDBに対して実行される単純なSQLクエリとデータにをプレゼンテーション層へ届けるためのDTOだけで構成される
- クエリスタックは出来るだけ単純に、上位レイヤーにはDTO(データ転送オブジェクト)を使って返す(それらはViewModel)
- 読み取りファサードは主にデータモデルとDBコンテキストによって構成される
- 高階関数で加工すれば、大量のDTOを作成せずに済む
コマンドスタックの実装
- コマンドとイベントの観点からワークフローを定義することでアプリケーションのユースケースを実装する
- ユーザからの入力などはコマンドバスに渡されるコマンドになる。さらに処理するには、コマンドバスがコマンドを登録済みのハンドラに渡す。コマンドの処理によりさらに別のコマンドとドメインイベントによって前進するプロセスが開始される
- コマンドをワークフローのコンテキストで処理するプロセスをSagaと呼ぶ
- Sagaはコマンドバスと連携することで、ユースケースを実装するために実行しなければならないタスクの手はずを整える
- コマンドをワークフローのコンテキストで処理するプロセスをSagaと呼ぶ
12. イベントソーシングの紹介
- DDDはデータモデルの作成よりもドメインモデルの作成を推奨する
- CQRSによりコマンドをクエリから離した瞬間にタスクという観点から考えるようになる
- タスクはドメインイベントとアプリケーションイベントに深く結び付く
- イベントソーシングはイベントをツールとして使う
- ドメインモデルではなくイベントを永続化する
-
イベントを中心に考える - 様々な出来事(イベント)の結果、状態が変更する - 状態はイベントの結果にすぎず、本質ではないのでないかという考え方
イベントを記憶する(状態は記憶しない)
- 何が起きたかだけを淡々と記憶する
- イベントソーシングの凄い所
- 完全な履歴が手に入る
- 任意の時点を再現出来る
- 問題点
- パフォーマンス(最新の状態を得るため、毎回膨大な量のイベントをリプレイスする必要がある)
- バージョニング(仕様変更が起きたら過去のイベントをどう処理するか)
- 実装が複雑(イベントを記憶してリプレイスする機構)
イベントの躍進
- イベントソーシングに言わせれば、イベントが発生すればそのイベントに関連付けられているデータの集まりが保存さる
- イベントの永続化に対処するには、イベントを記憶するためのイベントストア要素が必要になる
イベントソーシングアーキテクチャ
- イベントの再生
- ビジネスエンティティの状態を再現する
- 多くのシナリオでは、イベントの再生は非同期処理の一部
- 多くの場合は、数10個程度のイベントが対象となる為それほど時間はかからない、それが問題となるならデータスナップショットを使用する
- スナップショットはシリアライズされた状態の集約を含む、永続キャッシュ
13. イベントソーシングの実装
- 「最後に確認された正常な状態」ではなく、「発生した出来事」を追跡する方がビジネスとして意味がある場合には、イベントソーシングは妥当なパターンとなる
- イベントソーシングの実装 +α
- 追跡を目的としてイベントを記憶する + 対象となる集約の「最後に確認された状態」も保存する
イベントソーシングを使用する理由と状況
- イベントソーシングによりシステムの周りで発生するあらゆる事を追跡出来るようになり、アプリケーションロジックが処理するコンテキスト/コンテンツをアーキテクトが推測できるだけの柔軟性が確保出来る
イベントソーシングに適したシナリオ
Command Bus
public class Bus { private static readonly Dictionary<Type, Type> SagaStarters = new Dictionary<Type, Type>(); private static readonly Dictionary<String, Object> SagaInstances = new Dictionary<String, Object>(); private static readonly EventRepository EventStore = new EventRepository(); public static void RegisterSaga<TStartMessage, TSaga>() { SagaStarters.Add(typeof(TStartMessage), typeof(TSaga)); } public static void Send<T>(T message) where T : Message { // Implement } }
- Saga: Eventを処理するプロセス
public class MatchSaga : SagaBase<MatchData>, IStartWithMessage<MatchStartedEvent>, ICanHandleMessage<MatchEndEvent>, ICanHandleMessage<PeriodStartedEvent> { private readonly EventRepository _repo = new EventRepository(); public void Handle(MatchStartedEvent, message) { // SagaのIDを設定 SageId = message.MatchId; // 試合情報が更新されている事を通知 NotifyMathcInfoChanged(message.MatchId); } public void Handle(MatchEndEvent, message) { // 試合情報が更新されている事を通知 NotifyMathcInfoChanged(message.MatchId); } }
// このmessageがbusにpushされるとsagaが起動する var domainEvent = new MatchStartedEvent(matchId); Bus.Send(domainEvent);
- イベントの永続化
EventRepository
- 通常、イベントはDTO(プロパティ)の集まりであるので、DBのレコードとして簡単に保存可能
- イベントの再生
- 記録されたイベントから集約の状態を再現、一連のイベントをループで処理する
public IEnumerable<EventMapper> GetEventStream(String id) { return DocumentSession.Query<EventWrapper>() .Where(t => t.MatchId == id) .OrderBy(t => t.Timestamp).toList(); }
集約snapshotによるイベントソーシング
- イベントソーシングは自然な選択でないといけない。イベントを追跡する事が重要でその方が簡単であるなら、イベントソーシングを利用しそこから状態を再現するか、集約をスナップショットとして永続化するか
see also
「Application Architecture for .NET」を読んだ - 3部(前編)
.NETのエンタープライズアプリケーションアーキテクチャ 第2版 (マイクロソフト公式解説書)
- 作者: Dino Esposito,Andrea Saltarello,日本マイクロソフト(監訳),クイープ
- 出版社/メーカー: 日経BP社
- 発売日: 2015/06/04
- メディア: 単行本
- この商品を含むブログ (2件) を見る
「Application Architecture for .NET」を読んだ - 1部
「Application Architecture for .NET」を読んだ - 2部
の続き
第三部サポートアーキテクチャ(8, 9章)のメモ。
TL;DL
- ドメインモデル
- エンティティはビジネス空間に関連するオブジェクトであり同一性とライフタイムを持つ
- 値オブジェクトは空間内の無生物とも言える存在
- 一部のエンティティはコーディグや設計上の目的で集約に纏められる
- モデリングの際の一番の関心毎ではないものの、DB構造が制約である
3. サポートアーキテクチャ
8. ドメインモデルの紹介
データから振る舞いへの移行
- DDDはレコードの代わりにオブジェクトを使ってデータアクセスコードを記述するだけのアプローチではない
- ドメインモデルはビジネスのAPI
- クラスを基本的なビジネスコンセプトと一致させ、常に整合性を保つ事 => データより振る舞いに重点を置く
- DDDはドメインモデルの作成を優先し、モデリングされたドメインの永続化は後回しにする
- ドメインモデルは永続化に関与しない
ドメイン層の内側
- アプリケーション層はドメインとインフラ層を操作するためのオーケストレーションコード
- ドメインモデル: エンティティ/値オブジェクトから構成される
- モジュール: ドメインモデルを纏める名前空間
- リポジトリ: エンティティの永続化
- 契約(コンストラクト)はドメイン層、実装はインフラ層
集約
- モデル内のエンティティを纏めたり区切ったりする整合性の境界
ドメインモデルでは、複数のモデルを1つのコンテナに纏めたものを集約と呼ぶ
「集約とは、データを変更する目的で1つの単位として扱われる、関連するオブジェクトの集まりです」
- エンティティが参照できるのは、同じ集約内のエンティティか、別の集約のルートだけ
- 集約のルートの責務
- カプセル化されている全てのオブジェクトの永続化に対する責任
- クエリ操作で取得できるのは集約のルートだけ、内部オブジェクトへのアクセスは集約のルートのインターフェースを通じて発生しなければならない
// 集約ルートを示すmarker // 集約ルートだけがリポジトリを持つ、リポジトリの型制約として使う public interface IAggregateRoot { bool CanBeSaved { get; } } public class Order: IAggregateRoot { bool IAggregateRoot.CanBeSaved { get { return validate(); } } // ... }
ドメインサービス
- ドメインロジック(特定の集約に属さない、ほとんどの場合複数のエンティティにまたがっている)を実装するメソッドを持つクラス
- リポジトリ
- 永続化の為に存在する
- CustomerRepositoryのように集約ルート毎にリポジトリを1つ使用することになる
public interface IRepository<T> where T: IAggregateRoot { // interfaceを単なるmarker interfaceにしても良いし、一般的なメソッドを幾つか定義することも出来る T Find(object id); void Save(T item); }
public interface IDomainEvent {} public class CustomerReachedGoldMemberStatus: IDomainEvent { public Customer customer { get; set; } }
- 横断的関心事
9. ドメインモデルの実装
実用的なドメインモデルを作成するための護身術
- ドメインモデル貧血症
- エンティティが貧血と判断されるのは、エンティティに属しているものの¥、エンティティクラスの外側に配置されているロジックがある場合
- ex. メソッドがエンティティのメンバーのみを扱う場合は、そのエンティティに属している
DateTime EstimatedPayment(Invoice invoice)
- ex. メソッドがエンティティのメンバーのみを扱う場合は、そのエンティティに属している
- エンティティが貧血と判断されるのは、エンティティに属しているものの¥、エンティティクラスの外側に配置されているロジックがある場合
- エンティティのscaffolding
- 値オブジェクトのscaffolding
- 集約の特定
- 特別なケース
- モデルの永続化
「Application Architecture for .NET」を読んだ - 2部
.NETのエンタープライズアプリケーションアーキテクチャ 第2版 (マイクロソフト公式解説書)
- 作者: Dino Esposito,Andrea Saltarello,日本マイクロソフト(監訳),クイープ
- 出版社/メーカー: 日経BP社
- 発売日: 2015/06/04
- メディア: 単行本
- この商品を含むブログ (2件) を見る
「Application Architecture for .NET」を読んだ - 1部の続き
第二部アーキテクチャの考察のメモ。
TL;DL
- Domainを理解することは適切なアーキテクチャの発見に繋がる
- スムーズで流れるようなプロセスを中心に、ユーザタスクを構造化するのが重要
- ビジネス層をドメイン空間の現実のプロセスと忠実に一致させるのが重要
- 正しい事を行う事と、物事を正しく行う事
- 一方がもう一方から生じるのではなく、どちらも成し遂げる事が重要
- 正しい事を行う事と、物事を正しく行う事
5. ドメインアーキテクチャの発見
- ソフトウェアには現実の世界を正確に映し出すのが期待される
- ソフトウェアが現実世界のどの部分(Domain)をモデリングしているのか理解する
DDDの本当の付加価値
- DDDの目的は「ソフトウェアの核心にある複雑さに立ち向かう」
- 付加価値は、ビジネスcontextの境界を定義するために分析部分を使用することにある
- 境界付けられたcontext
- Domainの様々な領域の内、独自のユビキタス言語を持つため別個に扱った方が効果的な領域
ユビキタス言語
- 言葉の壁は業務の妨げにならないかもしれないが、進行を遅らせる
- 専門用語をcontextに合わせて調整する
- ユビキタス言語の目的は全ての関係者が全てのレベルで使用する共通用語を定義すること
- ビジネスcontextから開発contextに翻訳する必要が無くなり、明確さが促進され仮定しなければならないことが最小限に抑えられる
- ユビキタス言語はプロジェクトの公式言語
境界付けられたcontext
- 独自のユビキタス言語とアーキテクチャを必要とするアプリケーション領域
- 関係パターン
- 上流context(u): 下流に影響を与える、下流を強制的に変更する場合もある
- 下流context(d): 受動的、上流contextによって変更される
階層化アーキテクチャ
- Presentation層
- タスクを実行するユーザーインターフェースを提供(画面の集まり)
- Presentation層に追加される
ViewModel
== 画面から送信されバックエンドアクションを開始するApplication層の入力Model
- Application層
- Domain層
- Infrastructure層
- 永続化レイヤー、データアクセス層、具体的なテクノロジの使用に関するあらゆるもの
6. プレゼンテーション層
- 最初にプレゼンテーションに取り組めば、処理すべき注文やそれらの処理方法を素早く効果的に理解するのに役立つ
ユーザーエクスペリエンスファースト
- データではなくやり取りに焦点を当てるべき(UIとUXの違い)
- タスクベースの設計 => CQRS
- UIをUXに置き換える
- UXはUI以上のもの
- ユーザが操作しているのを見るまで、提案したUXの良し悪しを正確に判断することは出来ない
シナリオ
- controllerはApplication層やBusiness層の一部と見なすのではなく、出来るだけ薄く保つようすべき
- ApplicationService: ワーカーサービス(controllerとマッチする)
- Webサービスのデバイスサポート
- 機能検出による分岐(localStorageを対応していたらHTML5扱いみたいな)
- クライアント側でのデバイス検出によるレスポンシブの実現
- SPA
- ユーザアクションに続くタスクのオーケストレーションをクライアント側で行う
7, 伝説のビジネス層
- 従来のデータ中心の3層構造から、モデル中心のマルチレイヤーアーキテクチャーへ、イベント駆動型のアーキテクチャーへ
- Transaction Script Pattern
- Domain Model Pattern
- 期待される振る舞いと、動作させるデータの流れに焦点を合わせるアーキテクチャー(クラスという概念から再現する)
- Domainについて知れば知るほどそれをソフトウェアで模倣しやすくなる
- エンティティのプロパティよりもアクションに目を光らせる => 振る舞いを突き止めるのに役立つ(Tell Don't Ask)
- ADM(Anemic Domain Model)(アンチ) Pattern
- エンティティに含まれているのはプロパティだけで振る舞いがない
データからタスクへの焦点の移動
- Domain内でのタスクオーケストレーション
- ドメインモデルに存在しないデータの集まりをユーザインターフェースに持ち込みたいかもしれない etc => DTOを使う
- DTOは振る舞いを持たない単純なコンテナ
- エンティティからDTOを作成する
- AutoMapper, Adapter
- DTOは振る舞いを持たない単純なコンテナ
Mapper.CreateMap<Source, DTO> var dto = Mapper.Map<DTO>(sourceObj)