OsloとM言語(続き)

DBFluteSubSonic、MyGenerationはテーブルからソースコードを生成しますが、M言語はモデルから、テーブルとソースコードT-SQL)を生成できます。
M言語は、以下のようなテキストでモデリングを行います。

// hello_m.m                       // コメント

module MyModule {                  // モジュール

  type Person {                    // エンティティ
    Id : Integer64 = AutoNumber(); // Identityフィールド
    Name : Text;                   // 基本型のフィールド
    LuckyNumbers : Integer32*;     // コレクションのフィールド
    Size : Size;                   // カスタムエンティティのフィールド
  } where identity Id;
  
  type Size {
    Id : Integer64 = AutoNumber();
    Height : Integer32 where value < 10; // 制約(バリデーション)付のフィールド
    Weight : Integer32 = 150;            // 既定値(デフォルト)付のフィールド
  } where identity Id;
  
  Stones(Weight : Decimal9) { Weight * 0.07 }   // 関数(重さ×0.07)

  Sizes : Size* { {Height = 6, Weight = 250} }; // インスタンスのデータ(レコード)

  People : Person* where item.Size in Sizes;    // 制約(Peopleテーブルのレコード条件)

}

M言語ではビジネスモデル的(ドメインモデル的)な記述ができます。この例では、関数、インスタンスデータ、制約が設定されています。
生成されるSQLの一部は次の通りです。他にもいろいろトリガやVIEWが生成されます。

create table [MyModule].[PeopleTable]
(
  [Id] bigint not null identity,
  [Name] nvarchar(max) not null,
  [Size] bigint not null,
  constraint [PK_People] primary key clustered ([Id]),
  constraint [FK_People_Size_MyModule_Sizes] foreign key ([Size]) references [MyModule].[SizesTable] ([Id])
);

create table [MyModule].[SizesTable]
(
  [Id] bigint not null identity,
  [Height] int not null,
  [Weight] int not null default 150,
  constraint [PK_Sizes] primary key clustered ([Id]),
  constraint [Check_Sizes] check ([MyModule].[Check_Sizes_Func]([Id], [Height], [Weight]) = 1)
);

create function [MyModule].[Stones]
(
  @Weight as decimal(9,6)
)
returns decimal(28,6)  as
  begin
    return @Weight * 0.07
  end

create function [MyModule].[Check_Sizes_Func]
(
  @Id as bigint
,   @Height as int
,   @Weight as int
)
returns bit  as
  begin
    return case
  when @Height < 10 then 1
  else 0
  end

insert into [MyModule].[Sizes] ([Height], [Weight])
 values (6, 250)
;
go

VisualStudioを使う代わりに、テキストエディタ「Intellipad」が用意されています。これで、営業担当者やビジネスアナリストでもモデリングをしやすくなります。

Intellipadでは動的に、生成するSQLを見たり、記述したM言語のASTを見たりできます(図の下がSQL、右がAST)。
このASTはMXファイルに含まれる「compilationunit.xaml」と同じだと思います。MXファイルはコンパイル後のオブジェクトファイルのようなものですが、コメントなども含まれているので、たぶん、MXファイルからは、M言語にリバースしたり、SQLを生成したり、GUI(Quadrant)との相互変換にも使われるのではないかと思います。

M言語では上記のような静的モデリングと、インスタンス操作に加えて、以下のような動的な操作(ロジック)も書けます。

module Linq001
{
    Numbers : Integer32* { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0}
    
    LowNums1() {  //データを取得するロジック
        from n in Numbers
        where n < 5
        select n
    }
    
}

文法はLinqですが、C#ではなくM言語で記述してコンパイルできます。(ただし、このCTPではSQL生成部は未実装)。
以下は別の例(NorthWind)です。CustomerOrderDetailがメソッドのようなものです。

module dbo
{
    (略)

    type [Order Detail]
    {
        Order : Order;
        Product : Product;
        UnitPrice : Decimal28; // = 0; 
        Quantity : Integer32 where value > 0 = 1;
        Discount : Decimal28 where value >= 0 && value <= 1;
    }

    [Order Details] : ([Order Detail]
                      where value.Order in Orders &&
                            value.Product in Products)*;

    CustomerOrderDetail(OrderID : Integer32)
    {
        [Order Details]
        where value.Order.Id == OrderID
        select
        {
            ProductName = value.Product.Name,
            UnitPrice = value.UnitPrice,
            Discount = (value.Discount * 100),
            ExtendedPrice = (value.UnitPrice * (1 - value.Discount) * value.Quantity)
        };
    }

    (略)

振る舞いの連鎖(手続き的な処理)については、WF(WorkFlow)と連携するのか、M言語に含まれるのかは、良く分かりませんでした。

最初に書いたようにM言語での記述は、SQL Serverのオブジェクトとして反映されます。最初は違和感があったのですが、これには次のようなメリットがあります。

  • 依存するものが無い。SQL Server 2008にさえアクセスできれば良い。クライアントは.NET2.0のADO.NETでもLINQでもPowerShellでもなんでもよい。JavaRubyでも良いはず。
  • M言語ではビジネスルールを記述できるので変なデータが登録されることがない。エンドユーザでもExcelやscaffold(ASP.NET Dynamic Data)を使って生データを操作できる。
  • SOAとかRestFulとか。

OsloとM言語はドメイン層とインフラ層とSQL Serverに詰め込みます。もしかしたらサービス層も詰め込めるかもしれません。M言語でモデリングするだけで、業務アプリケーションで記述していたビジネスロジックが全てがSQL Serverに反映されます。

M言語でのDSLの定義方法については、次回書きます。