Some questions.

Jul 6, 2008 at 6:34 PM

Hi, Tim!

First of all, thank you for this book! This is really first book for practical application of DDD.

I have some questions:

1)  Is it possible to use public constructors for domain object (Project, Contact, Company, etc) initialization? In the “Domain-Driven Design: Tackling Complexity in the Heart of Software” book Eric Evans wrote:

When a Constructor Is All You Need

 I've seen far too much code in which all instances are created by directly calling class constructors, or whatever the primitive level of instance creation is for the programming language. The introduction of FACTORIES has great advantages, and is generally underused. Yet there are times when the directness of a constructor makes it the best choice. FACTORIES can actually obscure simple objects that don't use polymorphism.

May be, it would be rather to create “factory method”

public static void CreateEntity(EntityBase entity, object key)

in the base class EntityBase and

Create{XXX}

for the derived classes, for example:

public static Contact CreateContact(object key, string firstName, string lastName)

        {

            Contact contactEntity = new Contact();

            CreateEntity(contactEntity, key);

            contactEntity.JobTitle = string.Empty;

            contactEntity.Email = string.Empty;

            contactEntity.PhoneNumber = string.Empty;

            contactEntity.MobilePhoneNumber = string.Empty;

            contactEntity.FaxNumber = string.Empty;

            contactEntity.Remarks = string.Empty;

            contactEntity.CurrentCompany = null;

            contactEntity.addresses = new List<Address>();

            return contactEntity;

        }


2) Is it safe to use the Object type for the Key property of EntityBase? May be it would be rather to use generics for Key (IEntity<TKey> and  EntityBase<TKey>, respectively)? And in this case the method NewKey() can be made as non static and virtual.


3) The pattern “Unit Of Work” is wonderful! But in the real applications it is often necessary to apply the changes to the several entities at the same time within the single transaction. If I get you right the UnitOfWork instance is created in Service class and it is required to use it in several RepositoryFactory at the same time?

Thanks,
Alexey Lizunov.

 

Novosibirsk, Russia

Jul 17, 2008 at 11:29 PM
Hi Alexy & Tim

I'm not as far along in the book as you seem to be Alexy. I'm a bit stuck on all the plumbing (infrastructure) issues up front, as usual, which for me is the main reason I am very interested in your book Tim. I find the domain layer comes more easily to me than all of the wiring you do need at the end of the day to make it work.

More related to your posting Alexy, I have seen other architectures that absolutely rely on factory constructors, so it is certainly possible (I'm thinking csla here, specifically).

I'm looking at the issues of the key as an object also, and thinking more along the lines of abstracting out the whole idea into a  separate key class. I'm really only interested in two types now, GUID and long. It would look something like the code below, and then I'd make it also responsible for the object overrides with EntityBase just deferring to its Key class for equality and hashing.

Ambitious book on a great subject, Tim. Looking forward to working through it.

Thanks,
BH
--------
   
    /// <summary>
    /// A key is any object that serves as the identity (key) of a <see cref="DomainObject"/>
    /// </summary>
    public abstract class Key<T> where T : struct
    {
        public T Value { get; set; }
        public bool IsEmpty { get { return Value.Equals(default(T)); } }
        public abstract Key<T> NewKey { get; }
    }

    public class KeyGuid : Key<Guid>
    {

        public KeyGuid() : this(Guid.Empty) { }
        public KeyGuid(Guid value) {
            Value = value;
        }

        public override Key<Guid> NewKey { get { return new KeyGuid(Guid.NewGuid()); } }
    }

    public class KeyLong : Key<long>
    {
        public KeyLong() : this(0) { }
        public KeyLong(long value) {
            Value = value;
        }

        // this is really meant to come from incrementing a value held in the db
        public override Key<long> NewKey { get { return new KeyLong(Value+1); } }
    }

Coordinator
Jul 21, 2008 at 5:35 AM

Alex,

Thank you for buying and reading my book!

Here are my answers:

1.  Yes, there is no problem with public constructors.  I am using the KISS principle here.  There are however several factory methods in the SmartCA.Infrastructure.Repositories assembly for creating domain objects.

2.  I decide to use the object type just because it was easy...that's what the CSLA uses (last time I checked anyways, which was a while ago).  I might decide to make it more concrete with a .NET Generics implementation as you have suggested.  Go ahead and make that a work item for me :).

3.  I agree about the Unit of Work implementation in this project...for a differnt way of doing this please look at my other DDD project here on CodePlex (in the related projects section).

Thanks,

Tim


AlexML wrote:

Hi, Tim!

First of all, thank you for this book! This is really first book for practical application of DDD.

I have some questions:

1)  Is it possible to use public constructors for domain object (Project, Contact, Company, etc) initialization? In the “Domain-Driven Design: Tackling Complexity in the Heart of Software” book Eric Evans wrote:

When a Constructor Is All You Need

 I've seen far too much code in which all instances are created by directly calling class constructors, or whatever the primitive level of instance creation is for the programming language. The introduction of FACTORIES has great advantages, and is generally underused. Yet there are times when the directness of a constructor makes it the best choice. FACTORIES can actually obscure simple objects that don't use polymorphism.

May be, it would be rather to create “factory method”

public static void CreateEntity(EntityBase entity, object key)

in the base class EntityBase and

Create{XXX}

for the derived classes, for example:

public static Contact CreateContact(object key, string firstName, string lastName)

        {

            Contact contactEntity = new Contact();

            CreateEntity(contactEntity, key);

            contactEntity.JobTitle = string.Empty;

            contactEntity.Email = string.Empty;

            contactEntity.PhoneNumber = string.Empty;

            contactEntity.MobilePhoneNumber = string.Empty;

            contactEntity.FaxNumber = string.Empty;

            contactEntity.Remarks = string.Empty;

            contactEntity.CurrentCompany = null;

            contactEntity.addresses = new List<Address>();

            return contactEntity;

        }


2) Is it safe to use the Object type for the Key property of EntityBase? May be it would be rather to use generics for Key (IEntity<TKey> and  EntityBase<TKey>, respectively)? And in this case the method NewKey() can be made as non static and virtual.


3) The pattern “Unit Of Work” is wonderful! But in the real applications it is often necessary to apply the changes to the several entities at the same time within the single transaction. If I get you right the UnitOfWork instance is created in Service class and it is required to use it in several RepositoryFactory at the same time?

Thanks,
Alexey Lizunov.

 

Novosibirsk, Russia

 




Coordinator
Jul 21, 2008 at 5:37 AM

Berry,

That's a great idea about the Key class, but I actually got bit on that on a different project where there were composite keys :).  I could do it in this project however.  Go ahead and submit it as a work item and I will work on it.

Thanks,

Tim


BerrylHesh wrote:
Hi Alexy & Tim

I'm not as far along in the book as you seem to be Alexy. I'm a bit stuck on all the plumbing (infrastructure) issues up front, as usual, which for me is the main reason I am very interested in your book Tim. I find the domain layer comes more easily to me than all of the wiring you do need at the end of the day to make it work.

More related to your posting Alexy, I have seen other architectures that absolutely rely on factory constructors, so it is certainly possible (I'm thinking csla here, specifically).

I'm looking at the issues of the key as an object also, and thinking more along the lines of abstracting out the whole idea into a  separate key class. I'm really only interested in two types now, GUID and long. It would look something like the code below, and then I'd make it also responsible for the object overrides with EntityBase just deferring to its Key class for equality and hashing.

Ambitious book on a great subject, Tim. Looking forward to working through it.

Thanks,
BH
--------
   
    /// <summary>
    /// A key is any object that serves as the identity (key) of a <see cref="DomainObject"/>
    /// </summary>
    public abstract class Key<T> where T : struct
    {
        public T Value { get; set; }
        public bool IsEmpty { get { return Value.Equals(default(T)); } }
        public abstract Key<T> NewKey { get; }
    }

    public class KeyGuid : Key<Guid>
    {

        public KeyGuid() : this(Guid.Empty) { }
        public KeyGuid(Guid value) {
            Value = value;
        }

        public override Key<Guid> NewKey { get { return new KeyGuid(Guid.NewGuid()); } }
    }

    public class KeyLong : Key<long>
    {
        public KeyLong() : this(0) { }
        public KeyLong(long value) {
            Value = value;
        }

        // this is really meant to come from incrementing a value held in the db
        public override Key<long> NewKey { get { return new KeyLong(Value+1); } }
    }




Aug 8, 2008 at 6:58 PM

Hi, Berry.

 

It’s an interesting idea to use generic Key class especially when using composite key as Tim noted. But there is a problem with passing the type-parameter “T” class Key<T>.

The construction “public abstract class EntityBase<TKey<T>>” which results is not very good because it is necessary to pass it to many types in the project. The main problem is concerned with the class UnitOfWork: this class has Dictionary<IEntity<TKey<T>> and Dictionary may include objects of different types. So this construction is impossible.

 

I would like to use nongeneric IKey interface or something like this.

Could you please show sample code with Key class.

 

Alex.

Aug 12, 2008 at 1:56 AM
Alex

Here's what I have so far with a non-generic key class, along with how that affects my Entity class (which I call a DomainObject). I haven't played with it as much as I'd like to yet - let me know what you think.

Berryl Hesh

CODE
-----------------
    public abstract class KeyBase : IEquatable<KeyBase>
    {
        public object Value { get; protected set; }
        public abstract bool IsEmpty { get; }

        public override string ToString() { return Value.ToString(); }

        public override bool Equals(object other) {
            if (other is KeyBase) return Equals(other as KeyBase);
            return false;
        }

        public bool Equals(KeyBase other) { return this == other; }

        public static bool operator ==(KeyBase lhs, KeyBase rhs) {
            // If both are null, or both are same instance, return true.
            if (ReferenceEquals(lhs, rhs)) {
                return true;
            }
            // If one is null, but not both, return false.
            if (((object)lhs == null) || ((object)rhs == null)) {
                return false;
            }

            return lhs.ToString().Equals(rhs.ToString());
        }

        public static bool operator !=(KeyBase lhs, KeyBase rhs) { return (!(lhs == rhs)); }

        public override int GetHashCode() { return Value.GetHashCode(); }
    }

    public class KeyGuid : KeyBase
    {
        public KeyGuid() : this(Guid.NewGuid()) { }
        public KeyGuid(Guid value) { Value = value; }
        public override bool IsEmpty { get { return (Guid.Empty == (Guid)Value); } }
    }

    public class KeyLong : KeyBase
    {
        // TODO: implement key table
        private const long mockDbLastValue = 47;

        public KeyLong() : this(mockDbLastValue + 1) { }
        public KeyLong(long value) { Value = value; }
        public override bool IsEmpty { get { return (0 == (long)Value); } }
    }
    /// <summary>
    /// Interface for a domain object that is known by a key.
    /// </summary>
    public interface IDomainObject
    {
        /// <summary>The identity of any DomainObject.</summary>
        KeyBase Key { get; }
    }

    public enum KeyPolicy { GUID, LONG }

    /// <summary>
    /// Layered supertype for all domain objects ensures that they are known
    /// by their identity alone (and not their attributes).
    /// </summary>
    public abstract class DomainObject : IDomainObject
    {
        public KeyBase Key { get; private set; }
        public static KeyPolicy KeyPolicy { get; set; }

        protected DomainObject() : this(null) {}

        protected DomainObject(KeyBase key) {
            Key = key ?? _newKey();
            _assertKeyTypeMatchesPolicy();
            _brokenRules = new List<BrokenRule>();
            _brokenRuleMessages = GetBrokenRuleMessages();
        }

        private static KeyBase _newKey() {
            switch(KeyPolicy)
            {
                case KeyPolicy.GUID:
                    return new KeyGuid();
                case KeyPolicy.LONG:
                    return new KeyLong();
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        private void _assertKeyTypeMatchesPolicy() {
            switch (KeyPolicy) {
                case KeyPolicy.GUID:
                    efAssert.ArgumentTypeRequired<KeyGuid>(Key);
                    break;
                case KeyPolicy.LONG:
                    efAssert.ArgumentTypeRequired<KeyLong>(Key);
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        #region Validation and Broken Rules

        private readonly List<BrokenRule> _brokenRules;
        private readonly BrokenRuleMessages _brokenRuleMessages;

        protected abstract void Validate();

        protected abstract BrokenRuleMessages GetBrokenRuleMessages();

        protected List<BrokenRule> BrokenRules {
            get { return _brokenRules; }
        }

        public ReadOnlyCollection<BrokenRule> GetBrokenRules() {
            Validate();
            return _brokenRules.AsReadOnly();
        }

        protected void AddBrokenRule(string messageKey) {
            _brokenRules.Add(new BrokenRule(messageKey, _brokenRuleMessages.GetRuleDescription(messageKey)));
        }

        #endregion

        #region Object Overrides

        public override string ToString() {
            return efObject.PackageClassDataToString(new KeyValuePair<string, object>("KEY", Key.ToString()));
        }
      
        public override bool Equals(object other) {
            if (other is DomainObject) return Equals(other as DomainObject);
            return false;
        }

        public bool Equals(DomainObject other) { return this == other; }

        public static bool operator ==(DomainObject lhs, DomainObject rhs) {
            // If both are null, or both are same instance, return true.
            if (ReferenceEquals(lhs, rhs)) {
                return true;
            }
            // If one is null, but not both, return false.
            if (((object)lhs == null) || ((object)rhs == null)) {
                return false;
            }

            // true if keys are equal
            return lhs.Key == rhs.Key;
        }

        public static bool operator !=(DomainObject lhs, DomainObject rhs) { return (!(lhs == rhs)); }

        public override int GetHashCode() { return Key != null ? Key.GetHashCode() : 0; }

        #endregion

Aug 15, 2008 at 1:36 PM

Hi, Berry.

 

Nevertheless I would like to use a generic class because it ensures type safety. But I couldn’t use it entirely because of constraints  of C#. And I’m working on this problem. When the results are got I will inform you.

If we fail to do this let’s give up. Because key property of Object type has enough functionality for being used in Entity class.

When I looked through your code I thought of using
delegate instead of mockDbLastValue. Repository will define Callback method and return real value for key. By default KeyLong class can use the value -1 or throw exception.     

 

Alex.

Aug 16, 2008 at 4:57 PM
Hi Alex

I refactored key generation into a static KeyFactory class and tweaked the collaborating classes a bit. It's not compiler type safe but it is run time type safe, certainly more so than using the object Key property. That just irks me a bit, especially since the balance of code that uses it assumes it will be a Guid anyway, so I'm going to stick with my Key class for now.

Berryl Hesh

----
CODE
----
    public enum KeyPolicy { GUID, LONG }

    internal static class KeyFactory
    {
        internal static KeyPolicy KeyPolicy { get; set; }

        static KeyFactory() {
            const string settingKey = "DomainObjectKeyTypePolicy";
            var configuredKeyPolicy = (string) ConfigurationManager.AppSettings[settingKey];
            KeyPolicy = (KeyPolicy)Enum.Parse(typeof(KeyPolicy), configuredKeyPolicy); ;
        }

        internal static KeyBase NewKey() {
            switch (KeyPolicy) {
                case KeyPolicy.GUID:
                    return new KeyGuid();
                case KeyPolicy.LONG:
                    return new KeyLong();
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        public static KeyBase ExistingKey(object key) {
            switch (KeyPolicy) {
                case KeyPolicy.GUID:
                    var guidValue = _assertGuidKey(key);
                    return new KeyGuid(guidValue);
                case KeyPolicy.LONG:
                    var longValue = _assertLongCompatibleKey(key);
                    return new KeyLong(longValue);
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        private static long _assertLongCompatibleKey(object key) {
            if (key is KeyLong) {
                var keyLong = key as KeyLong;
                return (long) keyLong.Value;
            }
            if (key is long) {
                return (long) key;
            }
            if (key is int) {
                return Convert.ToInt64(key);
            }
            //TODO: create KeyTypeException
            throw new ArgumentException(string.Format("Key type violation: cannot use '{0}' as a long value", key.GetType()));
        }

        private static Guid _assertGuidKey(object key) {
            if (key is KeyGuid) {
                var keyGuid = key as KeyGuid;
                return (Guid) keyGuid.Value;
            }
            if ((key is Guid)) {
                return (Guid) key;
            }
            throw new ArgumentException(string.Format("Key type violation: cannot use '{0}' as a Guid value", key.GetType()));
        }

    }

    ///<summary>
    /// Testing only; allows testing to change the Key Policy of the <see cref="KeyFactory"/>.
    ///</summary>
    public static class MockKeyFactory
    {
        public static KeyPolicy MockKeyPolicy {
            get{ return KeyFactory.KeyPolicy;}
            set { KeyFactory.KeyPolicy = value; }
        }
    }

-----

    /// <summary>
    /// Layered supertype for all domain objects ensures that they are known
    /// by their identity alone (and not their attributes).
    /// </summary>
    public abstract class DomainObject : IDomainObject
    {
        public KeyBase Key { get; private set; }

        protected DomainObject() : this(null) {}

        protected DomainObject(object key) {
            Key = key == null ? KeyFactory.NewKey() : KeyFactory.ExistingKey(key);
            _brokenRules = new List<BrokenRule>();
            _brokenRuleMessages = GetBrokenRuleMessages();
        }

        #region Validation and Broken Rules

        #region Object Overrides
 
    }
-------

    public class KeyGuid : KeyBase
    {
        public KeyGuid() : this(Guid.NewGuid()) { }
        public KeyGuid(Guid value) { Value = value; }
        public KeyGuid(KeyGuid key) { Value = key.Value; }
        public override bool IsEmpty { get { return (Guid.Empty == (Guid)Value); } }
    }

    public class KeyLong : KeyBase
    {
        // TODO: implement key table
        public const long mockDbNextValue = -1;

        public KeyLong() : this(mockDbNextValue) { }
        public KeyLong(long value) { Value = value; }
        public KeyLong(KeyLong key) { Value = key.Value; }
        public override bool IsEmpty { get { return (0 == (long)Value); } }
    }

Aug 25, 2008 at 4:32 AM

Hello.

 

In my opinion, I’ve managed to use generic class. The main idea is that  key value is kept in «_value » field of KeyBase class which has object type. Inheritor KeyBase<T>  class uses «Value» property of type T. Avoiding boxing  was not possible, but compiler type safety already works. I made the last static class TestObjects specially for test. If  the line entity.Key.Value = Guid.NewGuid(); is replaced by the line entity.Key.Value =0 the compiler error will occur.  To create domain class it is enough to specify key type (for example: public class Person : EntityBase<KeyGuid> or public class Contact : EntityBase<KeyBase<int>>), it is especially convenient for using composite keys.

 

 

Alex.

 

CODE:

 

public interface IEntity<T>

        where T : KeyBase

    {

        T Key { get; set;}

    }

 

    public interface IEntity : IEntity<KeyBase>

    {

 

    }

 

    public abstract class KeyBase : IEquatable<KeyBase>

    {

        protected object _value;

 

        public abstract bool IsEmpty();

 

        public bool Equals(KeyBase other)        {  }

    }

 

    public abstract class KeyBase<T> : KeyBase

        where T : struct

    {

        public abstract T Value { get; set; }

 

        public override bool IsEmpty()

        {

            return Value.Equals(default(T));

        }

    }

 

    public class KeyGuid : KeyBase<Guid>

    {

 

        public override Guid Value

        {

            get

            {

                return (Guid)_value;

            }

            set

            {

                _value = value;

            }

        }

    }

 

    public class EntityBase<TKey> : IEntity<TKey>

        where TKey : KeyBase

    {

        private TKey _key;

 

        public TKey Key

        {

            get { return _key; }

            set { _key = value; }

        }

    }

 

    public class Person : EntityBase<KeyGuid>

    {

 

    }

 

    public class Contact : EntityBase<KeyBase<int>>

    {

 

    }

 

    public static class TestObjects

    {

        public static void Test1()

        {

            Dictionary<KeyBase, IEntity> dict = new Dictionary<KeyBase, IEntity>();

 

            Person entity = new Person();

            entity.Key.Value = Guid.NewGuid();

            dict.Add(entity.Key, (IEntity)entity);

 

        }

    }

Aug 26, 2008 at 4:03 AM
Nice job Alex.

I had done a generic implementation also; the trade off seems to be the need to specify the generic parameter on each and every domain object. My thinking is that I want the Key to be consistent throughout the domain, and that I don't envision needing composite keys, or anything more than a Guid or a long. That's why I like a non generic solution with the notion of a central type 'policy' into a KeyFactory, the trade-off being a risk of type safety, which is small enough that I can live with it.

It also seems to me that Key is a natural excuse for a DDD Value type class, but it's tricky as it can easily tie some distracting plumbing stuff together with your Entities.

BH