2007年7月2日月曜日

grails でワークフロー (手で書く編2)

jpbm がとりあえず状態機械として動くことは判った. ワークフロー・エンジンとして使うためには (convention として) 組織モデルを考える必要がある. それをまずは決めておこう


組織モデル

ここでは組織モデルの作り方を決めましょう. 組織は永続してもらわないと困るので, とりあえずドメイン・クラスとして作ります (scaffold すればメンテナンス・フロントエンドができます). ただし Foo という組織は FooOrganization, Foo という組織のメンバは FooMember というクラス名にすることにします. また個人を表すドメイン・クラスは Person とします. いずれも後で楽ができることを期待した convention です.



Person.groovy の中身は例えばこんな感じになります.



class Person {

String name

String password

String email



String toString() { name }



static constraints = {

name(nullable:false, unique:true)

password(nullable:false)

email(nullable:true, email:true)

}

}


name と password が必要なこと以外は適当なプロパティがあっても構いません.



一方, 例えば Project という組織があるとしたら, ProjectOrganization.groovy という組織クラスを作ります.



class ProjectOrganization {

static belongsTo = ProjectOrganization

static hasMany = [subs:ProjectOrganization, members:ProjectMember]



ProjectOrganization zuper

String name



String toString() { name }



static constraints = {

name(nullable:false, unique:true)

zuper(nullable:true)

}

}


「組織は階層化している」「組織はメンバを持っている」のが肝です. やろうと思えばこれらは自動的に注入できるでしょう. その他のプロパティを必要に応じて追加しても構いません.



ProjectOrganization.groovy に対応して ProjectMember.groovy というメンバ・クラスを作ります.



class ProjectMember {

static roles = ['leader']

static belongsTo = [Person, ProjectOrganization]

static hasMany = [participations:Participation]



String role

Person person

ProjectOrganization project

List tasklist



static constraints = {

role(inList:roles, nullable:true)

person(nullable:false)

project(nullable:true)

}

}


roles は組織ごとに書く必要があります. また tasklist はワークフローで与えられたタスクが入るトレイになります. roles を除けば自動注入可能です. 組織と Person をつなぐ役割をしています.



テスト・コードはここには載せませんが, 使い方はまぁ自明でしょう. Person から自分がどの組織に属しているのか見えた方がいいかもしれません.



また今回は Project というような具体的な組織構造をクラス構造にハード・コードしました. 別の方法としてはもっと汎用的な組織構造だけをクラスにして, 個々の組織構造は実行時に組み立てるやり方もあります. 個々の組織構造が持つ振る舞いを組み込みたいと思ったので前者の方法を採りましたが, grails/groovy なら後から振る舞いを追加するのは可能でしたね.



さて, ようやくワークフローを定義することができます.