Mais conteúdo relacionado
Mais de Zenji Kanzaki (20)
業務システムのための型システム
- 2. わたしは…
㈱バリューソース
代表取締役
神崎 善司
zkanzaki@vsa.co.jp
はてな:good_way
twitter:@zenzengood
普段は
要件定義関連のコンサルテーション
セミナー開催(要件定義、オブジェクト指向、モデリング)
「要件のツボ」の開発
関数型との関係
7年くらい前から関数型に興味を持ち
CleanやF#をつまみ食いしながらScalaを発見!
以来サンデープログラマーとしてScalaを利用
「要件のツボ」のサーバーサイドで利用
札幌の技術者との交流の中で関数型がだんだんわかりかけてきた…
2
- 3. なにがしたいの?
ビジネスモデルを構造として定義する
ニーズ
コンサルの初期段階でお客さんのビジネスモデルを早く把握
する必要がある
ビジネスモデルの基本構造を記述
シナリオベースで動かして確認したい
コードにすることでメタ構造を洗い出す
- 4. ビジネスモデル 販売管理
BtoB 後払い商品販売 BtoC 前払いサービス提供
特定顧客に対して商品の販売 不特定多数の顧客に対して継続的なサービス提供
注文 注文
得意先 会社 顧客 会社
請求
与信
会員
登録 入金
商品
在庫引当
人の
サービス
検収 デリバリ 債権管理
ワークフロー管理
請求書 or
ライセンス
サービス
入金
サービス提供
- 5. ビジネスモデルによるデータ構造の違い
BtoB 後払い商品販売 BtoC 前払いサービス提供
特定顧客に対して商品の販売 不特定多数の顧客に対して継続的なサービス提供
注文
得意先 会社 顧
注文
会社
与信 請求
客
入金
会
商品 員
在庫引当 登
録 人の
検収 デリバリ 債権管理 サービス
ワークフ
ロー管理
請求書 or
ライセンス
サービ
サービス
ス提供
入金
前払: 請求 入金
受注 受注明細 受注・請求が同時
受注 受注明細 入金確認後出荷
・分割納品
・出荷まとめ
後払:
出荷 出荷明細 ・請求まとめ 受注 受注明細
・一部入金
検収確認後請求・入金
売上 請求 入金
検収 請求 入金
債権残高 相殺
売上 債権残高
債務残高
支払い
- 6. 販売管理 典型的なデータ構造
オブジェクトモデル クラスモデル
受注 受注明細
工程
受注 受注明細
受注明細
受注明細
・分割納品
明細単位で処理しな 出荷指示 出荷 出荷明細 ・出荷まとめ
い場合は受注・出荷 検収
で結びつく 売上
出荷指示 出荷 出荷明細
出荷 出荷明細
請求
出荷明細
分納がある場合は
債権残高 入金
受注1に対して出
相殺 ・請求まとめ
荷は多になる
受注 受注明細 ・一部入金
支払い 債務残高
複数の受注をまとめて
出荷する場合は出荷1 売上
売上 請求 入金
で受注多になる 入金
分割入金
債権残高 請求 入金
請求
前払いの場合 複数の請求分を
は債権残高 まとめて入金
がない (都度請求)
- 7. 例えば 販売管理のバリエーション
顧客との関係 単価の設定
見積・受注 → 見積もり・受注先 例)単価の決まり方
出荷 → 出荷先 商品に直接単価が紐尽く
請求・入金 → 請求先 得意先によって商品の単価が異なる
例)Webベースのサービス 商品 A単価
デリバリがないので出荷先が必要ない
請求先はメールだけで請求書送付がない
商品 得意先
例)前払い 後払い B単価
後払いには与信が必要
前払いは債権残高がない
~
ビジネスシステムは一つ一つは簡単だが、バリエー
ションと組み合わせが多岐にわたる
- 8. シナリオ 前払いのサービス提供
//田中商事(101)からリンゴ10個、ミカン20個を受注
val 田中商事受注 = CE受注(1,Date.valueOf("2012-01-21"),1,2)
受注機能.受注登録(田中商事受注)
//佐藤商事(102)からメロン20個、ミカン10個を受注
val 佐藤商事受注 = CE受注(2,Date.valueOf("2012-02-21"),2,3)
受注機能.受注登録(佐藤商事受注)
//出荷指示対象の受注一覧
受注機能.受注残一覧.foreach( e => println(e.body))
//田中商事の受注に対して出荷指示
val 田中商事出荷指示 = CE出荷指示(1,Date.valueOf("2012-01-22"),1)
出荷指示機能.出荷指示登録(田中商事出荷指示)
受注機能.受注残一覧.foreach( e => println(e.body))
単純なシナリオでビジネスモデルの確認を行う
- 10. Ord[T]
型クラス type class
Ord[MyModel] Ord[XXModel]
trait Ord[T] {
def compare (x: T, y: T): Boolean
}
case class MyModel(val data: Int)
MyModelのcompareの実装
implicit object ordMyModel extends Ord[MyModel] {
def compare (m1: MyModel, m2: MyModel) = m1.data <= m2.data
}
構造Tを型パラメータにもつOrd
def greedy[T](m1: T,m2: T)(implicit ordM: Ord[T]) = のサブクラス(Ord[MyModel])
if (ordM.compare (m1,m2)) m2 else m1 を割り当てる
-------------------
val m1 = new MyModel(3)
val m2 = new MyModel(5)
これは何????
greedy(m1,m2)
Sideways Coding:http://www.sidewayscoding.com/2011/01/introduction-to-type-classes-in-scala.html
- 11. 振る舞いを汎化し構造を型パラメータで変える
振る舞いを汎化で拡張 構造 構造は型パラメータによ
り任意のクラスを使える
振舞クラス[型]
Xxxxx[T <: 構造]
構造
型パラメータを汎化
構造
振舞の拡張[型拡張]
構造
振る舞いの拡張にあわ
せて型の拡張も可能
Ord[T]
Ord[MyModel]
implicit object ordMyModel extends Ord[MyModel] {
def compare (m1: MyModel, m2: MyModel) = m1.data <= m2.data
}
- 13. グラフ構造を抽象化する
trait Neighbors[Edge] {
def neighbors(e:Edge):List[Edge]
}
<Neighbors[Edge]> <trait> <object> <routes>
グラフ構造をお隣さんと
Neighbors[Edge] Route neighborsで得られたEdgeを使って経
いう概念で抽象化する
neighbors routes 路を導き出すロジックをもつ
実際の構造にあったお隣さんを返す
構造にあったNeighborsを与えることで任意の構
造に対応するNeighborsを処理できる
def routes[Edge](from:Edge,to:Edge) (implicit nghbrs:Neighbors[Edge]) :List[List[Edge]] ={
neighborsで得られたEdgeを使って経路を導き出す
}
- 14. 経路処理を実際の構造に注入する
trait Neighbors[Edge] { <trait> <object>
def neighbors(e:Edge):List[Edge] Neighbors Route
} neighbors routes
def routes[Edge](from:Edge,to:Edge)(implicit nghbrs:Neighbors[Edge]):List[List[Edge]] ={
外部からの入り口 def getRoutes(from:Base,to:Base):List[List[Base]] = from.routes(to)
Service implicit object BaseNeighbors extends Neighbors[Base]{
getRoutes def neighbors(base:Base):List[Base] = {
base.links.map(_.opposite(base)).toList
}
}
implicit def base2route(b:Base):{def routes(to:Base):List[List[Base]]} =
new Object{ def routes(to:Base):List[List[Base]] = Route.routes(b,to)
}
今回Edgeに対応するデータ構造
Base
edge1
Pathway Baseにroutes
edge2
routes links を後付けする
- 16. ビジネスモデルの要素を組み替える
BtoC 前払いサービス提供 BtoB 後払い商品販売
不特定多数の顧客に対して継続的なサービス提供 特定顧客に対して商品の販売
注文
注文
顧客 会社 会社
請求 得意先
与信
会員
登録 入金
商品
人の 在庫引当
サービス
検収 デリバリ
ワークフロー管理
債権管理
or 請求書
ライセンス
サービス
サービス提供 入金
<<消込残>> <<消込残>> <<消込残>> <<消込残>> <<消込残>>
出荷
見積 見積書 受注 注文書 ピッキング 納品書 出荷 請求書 入金
指示
リスト
見積もり 受注 出荷指示 出荷 請求書 入金
- 17. ビジネスモデルによってフローが変わる
<<消込残>> <<消込残>> <<消込残>> <<消込残>> <<消込残>>
出荷
見積 見積書 受注 注文書 ピッキング 納品書 出荷 請求書 入金
指示
リスト
見積もり 受注 出荷指示 出荷 請求書 入金
後払い商品販売
前払いサービス提供
- 18. importでコンテキスト変える
Import A or B ⇒ コンテキストを変える
<<消込残>> <<消込残>> <<消込残>> <<消込残>> <<消込残>>
出荷
見積 見積書 受注 注文書 ピッキング 納品書 出荷 請求書 入金
指示
リスト
見積もり 受注 出荷指示 出荷 請求書 入金
Business Layerの知
BtoB 後払い商品販売 識でプログラミング
特定顧客に対して商品の販売 A B
実装 implicit
ビジネスモデルの 見積 受注 出荷指示 出荷 請求書 入金
違いをコンテキス
トを変えて実現 Business Layer
管理単位
BtoC 前払いサービス提供
不特定多数の顧客に対して継続的な 消込 残高 少数の概念でBusiness
サービス提供 Layerを実現
リソース
Meta Layer
- 19. 実現
Import A or B ⇒ コンテキストを変える
APP
ドメインを利用するア
プリケーションはビジ
ネスレベルを利用する 一般的な振る舞い
と構造を定義する
ビジネスレイヤー
<<消込残>> <<消込残>> <<消込残>> <<消込残>> <<消込残>>
出荷
見積 見積書 受注 注文書 ピッキング 納品書 出荷 請求書 入金
指示
リスト
見積もり 受注 出荷指示 出荷 請求書 入金
メタレイヤー メタレイヤーで処理
消し込み 残高 の前後関係をつなぐ
ビジネスモデルとしての具体
コンクリートレイヤー: コンクリートレイヤー: 的な構造と振る舞いを定義
後払い商品販売 前払サービス提供
具体的な永続化の機
構もここで指定する
- 20. Meta Level
ビジネスの基本概念
・消込と残高という2つの概念でビジネスを捉える
・消し込み
受注→在庫引き当て 消込
商品を受注したら出荷
在庫引き当て→出荷 消込 して受注を消し込む
・残高
出荷→在庫減 残高
入荷→在庫増 残高
後続機能()
個々のEntityで消し込み済みのもの
<<trait>>
T消込操作
消込対象():Seq[Tentity]
消込済():Seq[T消込Wrapper]
消 消込 消込残():Seq[T消込Wrapper]
消込 込 消込 出 対象
クエリー(条件)
対象 受 対象 後続機能()
済 荷
注
消込残
消込残
- 22. コンクリートレベル
販売管理 後払い商品販売
型クラス モジュール
得意先 商品 組織
得意先 商品 組織
見積もり登録 注文を登録 出荷を指示する 出荷登録
<<消込残>> <<消込残>>
<<消込残>> 出荷 <<消込残>>
見積 受注 ピッキング 出荷 請求書 入金
見積書 指示 納品書
リスト
出荷 消込
<<CRUD>> <<消込残>>
受注 出荷指示登録 出荷残 請求・入金
見積もり
終端消込 終端消込
消込 消込 CRUD
R 残高
<<CRUD>> <<CRUD>> <<CRUD>> <<CRUD>>
受注登録 出荷予定 予定明細
見積登録 売上登録 <<加算>> <<減算>> 入金登録
<<消込残>> <<消込残>>
債権減 債権減
CRUD 見積残 CRUD 受注残 <<消込>>
R R 出荷指示消込済 CRUD 消込 CRUD
消込
見積もり 見積明細 受注 受注明細
U U
R <<消込>> 消込 売上 債権残高 入金
<<消込>> <<CRUD>>
見積消込済 出荷登録 CR
受注消込済
CRUD
<<請求残>>
請求データ
出荷 出荷明細 作成
<<消込>>
出荷 出荷消込済
在庫引き当て
在庫 入荷
- 23. コンクリートレベル
販売管理 前払いサービス提供
得意先 商品 組織
得意先 商品 組織
毎月繰り返される
受注 自動継続 入金
一定時間経過
受注 請求・入金 終端消込 サービス
消込
消込 <<CRUD>> 消込
<<CRUD>> 売上登録 <<消込>> <<CRUD>> サービス開始登録
受注登録 <<消込>> CRUD 請求消込 入金登録
受注消込済 CRUD
CRUD R 売上 CRUD
C 請求 サービス
R 入金
受注 受注明細
R
請求デー
契約情報 消込残 タ作成
R <<消込>>
<<消込残>> 請求残
受注残
X月X日YY
メール
請求書 サービスを
開始
- 25. <<trait>>
T消込操作
消込対象():Bool
基本構造 消込済():Seq[Entity]
消込残():Seq[Entity]
クエリー(条件)
Importにより切り替え
後続機能()
前払いサービス提供
シナリオ
型クラス
<<trait>>
後払い商品販売 コンクリート操作
<<trait>> <<trait>>
<<消込残>> 業務操作 コンクリート操作
見積 見積書
<<object>>
業務機能
見積もり implicit
後続機能() <<trait>>
コンクリート操作
<<trait>> <<trait>>
<<消込残>> 業務操作 コンクリート操作
受注 注文書
<<object>>
業務機能
受注 implicit
<<trait>>
後続機能() コンクリート操作
<<消込残>> <<trait>> <<trait>>
出荷
ピッキング 業務操作 コンクリート操作
指示
リスト <<object>>
業務機能
出荷指示
implicit
このレイヤーでは処理の前後関係を持たない このレイヤーでは処理の前後関係を持つ
- 26. 基本構造 <<trait>> <<trait>>
trait T受注消込[TENTITY <: TEntity] extends T消込操作 with TCRUD[TENTITY] {
T消込操作 TCRUD
消込対象():Seq[Tentity] 追加
消込済():Seq[T消込Wrapper] 参照
消込残():Seq[T消込Wrapper] 変更 前払いサービス提供
シナリオ クエリー(条件)
後続機能()
削除
persistence
後払い商品販売 <<trait>>
object 後払い商品販売シナリオ extends Application { コンクリート操作
import business1.後払い商品販売._
val 佐藤商事受注 = CE受注(2,Date.valueOf("2012-02-21"),2,3)
受注機能.受注登録(佐藤商事受注) <<trait>>
受注機能.受注残一覧.foreach( e => println(e.body)) T受注操作 <<trait>>
val 田中商事出荷指示 = CE出荷指示(1,Date.valueOf("2012-01-22"),1)
受注
出荷指示機能.出荷指示登録(田中商事出荷指示)
persistence
<<object>> 消込対象():Seq[TEntity]
シナリオ 受注機能 消込済():Seq[T消込Wrapper]
implicit 消込残():Seq[T消込Wrapper]
受注登録
受注残一覧 後続機能():出荷指示 <<trait>>
コンクリート操作
後続機能()
<<trait>>
T出荷指示操作 <<trait>>
<<object>> 出荷指示
出荷指示機能 Persistence
消込対象():Seq[TEntity]
implicit
消込済():Seq[T消込Wrapper]
消込残():Seq[T消込Wrapper]
後続機能():出荷指示
object 受注機能 {
def 受注登録[TENTITY <: TEntity](ent:TENTITY)(implicit p受注消込:T受注消込[TENTITY]): T受注消込[TENTITY]={
p受注消込.追加(ent)
p受注消込
}
def 受注残一覧[TENTITY <: TEntity](implicit p受注消込:T受注消込[TENTITY]) = p受注消込.消込残()
- 27. 基本構造 <<trait>> <<trait>>
T消込操作 TCRUD
消込対象():Seq[Tentity] 追加
消込済():Seq[T消込Wrapper] 参照
消込残():Seq[T消込Wrapper] 変更
シナリオ クエリー(条件)
後続機能()
削除
persistence
後払い商品販売
<<trait>>
<<object>> T受注操作 <<trait>>
受注機能 implicit 受注
受注登録 persistence
受注残一覧 消込対象():Seq[TEntity]
消込済():Seq[T消込Wrapper]
消込残():Seq[T消込Wrapper]
後続機能()
後続機能():出荷指示
object 後払い商品販売 {
implicit object 受注 extends T受注消込[CE受注] {
type TID = Int
type TMONEY = Int
type TENTITY = CE受注
val persistence = new Persistence[CE受注]
/** 対応する受注の集合を返す */
def 消込済() :Seq[T消込Wrapper] = {
for(ent1 <- 消込対象(); ent2 <- 後続機能().消込対象(); if ent1._id == ent2._受注id) yield ent1.empty消込Wrapper
}
/** 対応するものがない受注の集合を返す */
def 消込残() : Seq[T消込Wrapper] = {
消込対象().filterNot( jc1 => 後続機能().消込対象().exists( e => e._受注id == jc1._id)).map(e => e.empty消込Wrapper)
}
def 消込対象() : Seq[CE受注] = persistence.query( e => true )//全件返す
def 後続機能() = 出荷指示
}
- 28. 実装:Meta Layer&Business Layer
Meta Layer class 消込状態
object 未消込 extends 消込状態
object 消込中 extends 消込状態
object 消込済 extends 消込状態 Entity リソース
TEntity M商品 M顧客
消込 残高
<<trait>> id // //
<<trait>> 残高操作
T消込操作 追加
<<trait>> 参照 <<trait>> T消込Entity
消込対象():Seq[TEntity] T消込終端操作 変更 ParentEntity
消込済():Seq[TEntity] 削除 is消込対応() M管理単位 M組織
消込残():Seq[TEntity] 対応 明細
消込済()
消込可能 合計
加算一覧() 消込残() // //
クエリー(条件) 消込残 減算一覧 消込状態()
消込 前残高 //消込()
後続機能() 現残高
加算機能()
減算機能()
永続化
CRUD[T] Persistence
追加 create
参照 read
変更 update
削除 delete
persistence query
Business Layer
見積機能 受注機能 出荷指示機能 出荷機能 売上機能 在庫機能 債権残高機能 入金機能
見積登録 受注登録 出荷指示 出荷登録 売上登録 入荷 請求書発行 入金登録
見積一覧 受注一覧 出荷予定一覧 出荷一覧 売上一覧 出荷 債権残高 入金一覧
在庫
- 29. 実装クラス
メタレイヤー ビジネス コンクリート
<<trait>>
TCRUD
object
受注機能
レイヤー レイヤー
後払い商品販売シナリオ
追加
参照 受注登録
import 後払い商品販売
変更 受注残一覧
削除
persistence
trait 前払いサービス提供シナリオ
受注消込
<<trait>>
T消込操作 import前払いサービス提供
消込対象():Bool
消込済():Seq[Entity]
消込残():Seq[Entity]
クエリー(条件) 後払い商品販売
TE受注
後続機能() _id:TID 前払いサービス提供
implicit
_受日:Date object 受注
_顧客CD: TCID 出荷指示機能 受注
TParentEntity
_管理組織CD:TOCD
明細 消込対象
出荷指示登録 消込対象
消込済
合計 消込済(ent) 出荷指示残一覧 消込済
消込残
消込残(ent) 消込残
persistence
copy
T消込Entity
trait
T出荷指示消込 CE受注
消込済():T消込Entity
消込残(): T消込Entity 消込済(ent)
消込状態 消込残(ent)
empty消込Wrapper TE受注明細 copy
_商品:TID TE出荷指示
_数量:Date _id:TID
_金額: TCID implicit
TEntity _受日:Date 出荷指示
_出荷指示日:Date
id:TID _受注id: TID 出荷指示
出荷予定一覧
消込済(ent)
T消込Wraper 消込残(ent)
copy
id