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