C#と諸々

C#がメインで他もまぁ諸々なブログです
おかしなこと書いてたら指摘してくれると嬉しいです(´・∀・`)
つーかコメント欲しい(´・ω・`)

--/--/-- --:--
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
タグ:
トラックバック(-) | コメント(-) | このエントリーを含むはてなブックマーク
2011/06/05 23:57
先日公開した DDD サンプルアプリのアプリケーションアーキテクチャを、フレームワークとして CodePlex で公開しました。

DDDProjector - Home

フレームワークと言っても、クラスライブラリとしてではなく、C# ソリューション (を作成するツール) として提供します。なので、フレームワークのソースコードを直接カスタマイズすることが可能です。
サンプルアプリではデータストアに SQL CE 4.0、プレゼンテーション技術に Windows フォームを使用していましたが、このフレームワークでは特定のデータストア・プレゼンテーション技術には依存していません。

ツールを起動すると、ソリューション名や各プロジェクト名・名前空間の入力フォームが表示されます。
各フィールドを入力後、作成ボタンを押すと保存先選択ダイアログが出てきます。保存先を決定するとそこにソリューションが作成されます。
なお、入力値の妥当性検証は一切行っていないので注意してください。

これから少しずつ解説記事を公開していこうと思います。
スポンサーサイト
2011/05/20 00:42
DDD サンプルを公開しましたので解説記事とか書きたいのですが、その前に、同じアプリケーションアーキテクチャでアプリを開発するためのテンプレートか何かをこしらえようと思ってます。
ただ、プロジェクトのテンプレートは作成できてもソリューションのテンプレートは作成できないっぽいので、別の形を考えています。(複数プロジェクトを一つのテンプレートにすることはできるみたいですが、ソリューションフォルダの自由度が低いので。)

タグ:
2011/05/17 03:01
NHibernate + SQL Server Compact 4.0 + Windows フォームで作った DDD (Domain Driven Design: ドメイン駆動設計) のサンプルアプリを CodePlex で公開してみました。
Sandcastle で作った、アプリケーションアーキテクチャに関する説明ドキュメントも同梱してあります。
# ユニットテストは用意していません。

DDD サンプル - ToDoList - Home

2011/05/04 19:15

  • 列挙型のプロパティをマッピングできない
  • クエリー式で、列挙値が扱えない (int として扱わなければいけない)
  • クエリー式で、エンティティを直接比較できない (主キー等のプロパティを比較しなければいけない)
  • コレクションの順序を保存できない (順序を自分で管理する必要がある)
  • 関連オブジェクトの読み込みが非透過的
  • 関連オブジェクトの遅延読み込みが使いづらい (コレクションだけ遅延読み込みにするとかできない。そのためプロキシ作成を容認しなければいけない)
  • NHibernate の cascade=all-delete-orphan に相当する機能がない (集約内の子エンティティの削除はパーシステンスレイヤへ明示的に指示せずにできるべき)

Entity Framework 4.1 は試してないけど、この辺はそのままっぽい感じ
2番目のはロジックで弾きだした値を列挙型で扱おうとしてダメだった
最後のだけは何とかならんものか…

2011/04/06 10:56
DDD ではエンティティの構築をエンティティ自身が知っているのは不自然ということでファクトリの導入が推奨されているけれど、ファクトリよりビルダの方が向いているんじゃないかなぁ?
ファクトリだと、集約ルートの生成とそれ以外のエンティティの生成はアトミックに扱えないけどビルダならアトミックに扱えますし、構築中は不正な状態も扱えます。
また、構築に関するロジックがエンティティに一切なくなります。

ビルダは特に、UI への入力直後に入力チェックする場合や、ウィザード形式で入力を進めていく場合なんかに本領発揮します。

Person.cs
public sealed partial class Person
{
    private Person(string name, IList<Address> addresses)
    {
        _name = name;
        _addresses = addresses;
    }

    private string _name;

    private readonly IList<Address> _addresses;

    public string Name
    {
        get { return _name; }
    }

    public IEnumerable<Address> Addresses
    {
        get { return _addresses; }
    }
}

Address.cs
public sealed class partial Address
{
    ...
}

Person.Builder.cs
partial class Person
{
    public sealed class Builder
    {
        [Flags]
        public enum States
        {
            Valid = 0,
            NameIsEmpty = 1,
            NameIsTooLong = 2,
            AddressListIsEmpty = 4,
            AddressListHasError = 8,
        }


        public static readonly int MaxNameLength = 20;


        public Builder()
        {
            Name = null;
            AddressBuilders = new List<Address.Builder>();
        }

        public Builder(Person target)
        {
            if (target == null)
            {
                throw new ArgumentNullException("target");
            }
            Name = target.Name;
            AddressBuilders = target._addresses.Select(address => new Address.Builder(address)).ToList();
        }


        private readonly Person _target;

        public string Name;

        public readonly List<Address.Builder> AddressBuilders;


        public Address.Builder AddAddress()
        {
            var addressBuilder = new Address.Builder();
            AddressBuilders.Add(addressBuilder);
            return addressBuilder;
        }

        public void RemoveAddress(int index)
        {
            AddressBuilders.RemoveAt(index);
        }


        public States Validate()
        {
            return (ValidateName() | ValidateAddresses());
        }

        public States ValidateName()
        {
            if (string.IsNullOrWhitespace(Name))
            {
                return States.NameIsEmpty;
            }
            if (MaxNameLength < Name.Length)
            {
                return States.NameIsTooLong;
            }
            return States.Valid;
        }

        public States ValidateAddresses()
        {
            States result = States.Valid;
            if (AddressBuilders.Length == 0)
            {
                result |= States.AddressListIsEmpty;
            }
            foreach (var addressBuilder in AddressBuilders)
            {
                if (addressBuilder.Validate() != Address.Builder.States.Valid)
                {
                    result |= States.AddressListHasError;
                    break;
                }
            }
            return result;
        }


        public Person Build()
        {
            if (Validate() != States.Valid)
            {
                throw new InvalidOperationException();
            }

            var addresses = AddressBuilders.Select(builder => builder.Build()).ToList();

            if (_target == null)
            {
                return new Person(Name, addresses);
            }
            else
            {
                _target._name = Name;
                _target._addresses.Clear();
                foreach (var address in addresses)
                {
                    _target._addresses.Add(address);
                }
                return _target;
            }
        }
    }
}

Address.Builder.cs
partial class Address
{
    public sealed class Builder
    {
        [Flags]
        public enum States
        {
            ...
        }
        internal AddressBuilder() { ... }
        public States Validate() { ... }
        internal Address Build() { ... }
    }
}


まだ色々思案中でこれが本当に良い案なのかどうか確信は持ててないのですが。。。


[関連書籍]


ついにエリック・エヴァンスの "Domain-Driven Design" の日本語版が出版されますよ

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。