Skip to content

Instantly share code, notes, and snippets.

@nobeans
Last active June 24, 2017 07:50
Show Gist options
  • Save nobeans/1ab3fc72a3fea4cd30d6 to your computer and use it in GitHub Desktop.
Save nobeans/1ab3fc72a3fea4cd30d6 to your computer and use it in GitHub Desktop.
拡張GORM: GOREM(仮)

拡張GORM: GOREM(仮)

現状のGORMの課題

  • ドメインクラスのインスタンスを持ち回ると、セッション外等で面倒なことになる
  • だからといってDTOへの変換をするのはだるい(無駄なクラス、バインディングの手間、等)
  • 同一の項目定義で異なるテーブルに対応づける構成が直感的ではない(継承等で可能ではあるが)
  • 複数パターンのバリデーションルールを使い分けられない(2.xでプラグインはあったが)

GOREM

こういうGORM的APIがあると、DDD的にも通常利用でも非常に便利な気がする。 ポイントはドメインクラスに勝手にGORM的APIをはやすのではなく、明示的にRepositoryトレイトを実装させる、だけ。

// 普通のPOJO
// src/groovy配下でOK。Javaでも大丈夫。
class Book {
    String title
}

// grails-app/domain
trait BookRepository implements Repository<Book> {
    static constraints = { .... }
    static mapping = { table 'book' }
    static findByMyRule() { .... }
}

// 基本はリポジトリ経由でDB等のGORM的操作をする
BookRepository.list()
BookRepository.where { title == "HOGE" }.get()
BookRepository.add new Book(title: "新しい本")
BookRepository.validate book

// 同一のBookに対して複数のDBテーブルを定義できる
class OldBookRepository implements Repository<Book> {
    static constraints = { .... }
    static mapping = { table 'old_book' }
    static findByMyRule() { .... }
}

// 動的トレイト適用で一時的に従来GORMのようにも使える
def book = Book.get(1) as BookRepository
book.title = "変更"
book.validate()
book.save(flush: true)

// このように定義すれば、従来GORMとまったく同一になる
class Book2 implements Repository<Book> {
    String title
    static constraints = { .... }
    static mapping = { table 'book' }
    static findByMyRule() { .... }
}

// バリデーションのための制約は差し替え/上書きできる、とうれしい
// コマンドオブジェクトとして簡単に使い分けられるし、ビジネスロジック上複数のバリデーションルールを使い分けることも多い。
// (DDLやGSP生成はあくまでデフォルトバリデーションをベースとする)
// デフォルト制約のように定義しておいて、第2引数にクロージャを渡しても良い
BookRepository.validate(book) { title matches: /ADHOC/ }
(book as BookRepository).validate { title matches: /ADHOC/ }

備考

コア自体をこのように変更しつつ、設定ファイルでgrails.domain.autoImplementTraitみたいな項目を追加して、デフォルトtrueにすることで従来通りのドメインクラスを書けるようにして互換性を保持しつつ、falseにすると明示的にトレイトをimplementsしないとダメになって、上記のような使い方ができるようになる、という感じになると大変うれしい。

現状のGORMで強引にそれっぽく使ってみると...

// コンバート用トレイト
trait Repository<T, R> implements GormEntity<T> {
    static R of(T) {
        // ...
    }

    T get() {
        // ...
    }

    void add(T, params = [:]) {
        // ...
    }

    T asType() {
        // 強制型変換で変換可能にする
    }

    boolean validate(T, Closure constraints) {
        // ....
    }
}

// src/groovy配下のPOJO
class Book {
    String title
}

// grails-app/domain
trait BookRepository extends Book implements Repository<Book, BookRepository> {
    static constraints = { .... }
    static mapping = { table 'book' }
    static findByMyRule() { .... }
}

Book book = new Book(title: "新しい本")
BookRepository.add book, flush: true
//or BookRepository.of(book).save(flush: true)

Book book2 = BookRepository.get(1).get()

List<Book> books = BookRepository.list()*.get()

BookRepository.validate book { title blank: false }

苦しい。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment