Last active
November 28, 2016 21:55
-
-
Save hlaueriksson/61bbc512efaf3ecd9dd6622306843c13 to your computer and use it in GitHub Desktop.
2016-11-30-entities-and-value-objects-in-csharp-for-ddd
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using ConductOfCode.Domain; | |
using Machine.Specifications; | |
using Ploeh.AutoFixture; | |
namespace ConductOfCode.Specs | |
{ | |
public class AutoFixtureSpecs | |
{ | |
It AutoFixture_can_not_create_an_instance_if_validation_fails = () => | |
{ | |
var fixture = new Fixture(); | |
var exception = Catch.Exception(() => | |
fixture.Create<Customer>() | |
); | |
exception.ShouldContainErrorMessage("Exception has been thrown by the target of an invocation."); | |
exception.InnerException.ShouldContainErrorMessage("Email is invalid"); | |
}; | |
It AutoFixture_can_not_create_an_instance_using__With__if_the_property_is_read_only = () => | |
{ | |
var fixture = new Fixture(); | |
var exception = Catch.Exception(() => | |
fixture.Build<Customer>().With(x => x.Email, new Email("a@b.c")).Create() | |
); | |
exception.ShouldContainErrorMessage("The property \"Email\" is read-only."); | |
}; | |
It AutoFixture_can_create_an_instance_using__Register__and_a_creation_function = () => | |
{ | |
var fixture = new Fixture(); | |
fixture.Register(() => new Email("a@b.c")); | |
var subject = fixture.Create<Customer>(); | |
subject.ShouldNotBeNull(); | |
}; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using AutoMapper; | |
using ConductOfCode.Data; | |
using ConductOfCode.Domain; | |
using Machine.Specifications; | |
namespace ConductOfCode.Specs | |
{ | |
public class AutoMapperSpecs | |
{ | |
Establish context = () => | |
{ | |
var config = new MapperConfiguration(cfg => | |
{ | |
cfg.CreateMap<CustomerDto, Customer>().ReverseMap(); | |
cfg.CreateMap<NameDto, Name>().ReverseMap(); | |
cfg.CreateMap<EmailDto, Email>().ReverseMap(); | |
}); | |
_mapper = config.CreateMapper(); | |
}; | |
It AutoMapper_can_map_to_the_entity = () => | |
{ | |
var dto = new CustomerDto | |
{ | |
Id = Guid.NewGuid(), | |
Name = new NameDto { First = "Sherlock", Last = "Holmes" }, | |
Email = new EmailDto { Value = "sherlock@holmes.co.uk" } | |
}; | |
var result = _mapper.Map<Customer>(dto); | |
result.ShouldNotBeNull(); | |
}; | |
It AutoMapper_can_not_map_to_the_entity_if_validation_fails = () => | |
{ | |
var dto = new CustomerDto | |
{ | |
Id = Guid.NewGuid(), | |
Name = new NameDto { First = "Sherlock", Last = "Holmes" }, | |
Email = new EmailDto { Value = "invalid" } | |
}; | |
var exception = Catch.Exception(() => | |
_mapper.Map<Customer>(dto) | |
); | |
exception.ShouldContainErrorMessage("Email is invalid"); | |
}; | |
It AutoMapper_can_map_from_the_entity = () => | |
{ | |
var entity = new Customer(Guid.NewGuid(), new Name("Sherlock", "Holmes"), "sherlock@holmes.co.uk"); | |
var result = _mapper.Map<CustomerDto>(entity); | |
result.ShouldNotBeNull(); | |
}; | |
static IMapper _mapper; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[DebuggerDisplay("{Email}")] | |
public class Customer | |
{ | |
public Guid Id { get; } | |
public Name Name { get; } | |
public Email Email { get; } | |
public Customer(Guid id, Name name, Email email) | |
{ | |
if (id == Guid.Empty) throw new Exception("Id is invalid"); | |
if (name == null) throw new Exception("Name is required"); | |
if (email == null) throw new Exception("Email is required"); | |
Id = id; | |
Name = name; | |
Email = email; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using ConductOfCode.Domain; | |
using Machine.Specifications; | |
namespace ConductOfCode.Specs | |
{ | |
[Subject(typeof(Customer))] | |
public class CustomerSpecs | |
{ | |
public class Ctor | |
{ | |
It should_create_an_instance_with_valid_state_via_ctor_parameters = () => | |
{ | |
var subject = new Customer(Guid.NewGuid(), new Name("Sherlock", "Holmes"), new Email("sherlock@holmes.co.uk")); | |
subject.ShouldNotBeNull(); | |
}; | |
It should_be_able_to_use_conversion_operators = () => | |
{ | |
var subject = new Customer(Guid.NewGuid(), new Name("Sherlock", "Holmes"), "sherlock@holmes.co.uk"); | |
subject.ShouldNotBeNull(); | |
}; | |
It should_validate__Id__ = () => Catch.Exception(() => new Customer(Guid.Empty, new Name("Sherlock", "Holmes"), "sherlock@holmes.co.uk")).ShouldContainErrorMessage("Id is invalid"); | |
It should_validate__Name__ = () => Catch.Exception(() => new Customer(Guid.NewGuid(), null, "sherlock@holmes.co.uk")).ShouldContainErrorMessage("Name is required"); | |
It should_validate__Email__ = () => Catch.Exception(() => new Customer(Guid.NewGuid(), new Name("Sherlock", "Holmes"), null)).ShouldContainErrorMessage("Email is required"); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[DebuggerDisplay("{Value}")] | |
public class Email : IEquatable<Email>, IEquatable<string> | |
{ | |
public string Value { get; } | |
public Email(string value) | |
{ | |
if (!value.Contains("@")) throw new Exception("Email is invalid"); | |
Value = value; | |
} | |
#region Conversion | |
public static implicit operator string(Email value) | |
{ | |
return value.Value; | |
} | |
public static implicit operator Email(string value) | |
{ | |
return new Email(value); | |
} | |
#endregion | |
#region Equality | |
public override bool Equals(object obj) | |
{ | |
var other = obj as Email; | |
return other != null ? Equals(other) : Equals(obj as string); | |
} | |
public bool Equals(Email other) => other != null && Value == other.Value; | |
public bool Equals(string other) => Value == other; | |
public static bool operator ==(Email a, Email b) | |
{ | |
if (ReferenceEquals(a, b)) return true; | |
if (((object)a == null) || ((object)b == null)) return false; | |
return a.Value == b.Value; | |
} | |
public static bool operator !=(Email a, Email b) => !(a == b); | |
#endregion | |
public override int GetHashCode() => Value.GetHashCode(); | |
public override string ToString() => Value; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using ConductOfCode.Domain; | |
using Machine.Specifications; | |
namespace ConductOfCode.Specs | |
{ | |
[Subject(typeof(Email))] | |
public class EmailSpecs | |
{ | |
public class Ctor | |
{ | |
It should_create_an_instance_with_valid_state_via_ctor_parameter = () => new Email("a@b.c").ShouldNotBeNull(); | |
It should_validate_the_email = () => Catch.Exception(() => new Email("invalid")).ShouldContainErrorMessage("Email is invalid"); | |
} | |
public class Conversion | |
{ | |
It should_convert_Email_to_string = () => | |
{ | |
string result = new Email("a@b.c"); | |
result.ShouldEqual("a@b.c"); | |
}; | |
It should_convert_string_to_Email = () => | |
{ | |
Email result = "a@b.c"; | |
result.Value.ShouldEqual("a@b.c"); | |
}; | |
} | |
public class Equality | |
{ | |
It should_be_equatable_with_another_Email = () => | |
{ | |
new Email("a@b.c").Equals(new Email("a@b.c")).ShouldBeTrue(); | |
new Email("a@b.c").Equals((object)new Email("a@b.c")).ShouldBeTrue(); | |
new Email("a@b.c").Equals(new Email("c@b.a")).ShouldBeFalse(); | |
new Email("a@b.c").Equals((Email)null).ShouldBeFalse(); | |
}; | |
It should_be_equatable_with_string = () => | |
{ | |
new Email("a@b.c").Equals("a@b.c").ShouldBeTrue(); | |
new Email("a@b.c").Equals((object)"a@b.c").ShouldBeTrue(); | |
new Email("a@b.c").Equals("c@b.a").ShouldBeFalse(); | |
new Email("a@b.c").Equals((string)null).ShouldBeFalse(); | |
}; | |
It should_support_equality_operator = () => | |
{ | |
(new Email("a@b.c") == new Email("a@b.c")).ShouldBeTrue(); | |
(new Email("a@b.c") == "a@b.c").ShouldBeTrue(); | |
(new Email("a@b.c") == new Email("c@b.a")).ShouldBeFalse(); | |
(new Email("a@b.c") == "c@b.a").ShouldBeFalse(); | |
}; | |
It should_support_inequality_operator = () => | |
{ | |
(new Email("a@b.c") != new Email("a@b.c")).ShouldBeFalse(); | |
(new Email("a@b.c") != "a@b.c").ShouldBeFalse(); | |
(new Email("a@b.c") != new Email("c@b.a")).ShouldBeTrue(); | |
(new Email("a@b.c") != "c@b.a").ShouldBeTrue(); | |
}; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using ConductOfCode.Domain; | |
using Machine.Specifications; | |
using Newtonsoft.Json; | |
namespace ConductOfCode.Specs | |
{ | |
public class JsonSpecs | |
{ | |
It JsonConvert_can_serialize_the_entity = () => | |
{ | |
var entity = new Customer(Guid.NewGuid(), new Name("Sherlock", "Holmes"), "sherlock@holmes.co.uk"); | |
var result = JsonConvert.SerializeObject(entity); | |
result.ShouldNotBeEmpty(); | |
}; | |
It JsonConvert_can_deserialize_the_entity = () => | |
{ | |
var json = JsonConvert.SerializeObject(new Customer(Guid.NewGuid(), new Name("Sherlock", "Holmes"), "sherlock@holmes.co.uk")); | |
var result = JsonConvert.DeserializeObject<Customer>(json); | |
result.ShouldNotBeNull(); | |
}; | |
It JsonConvert_can_not_deserialize_the_entity_if_validation_fails = () => | |
{ | |
var json = JsonConvert.SerializeObject(new Customer(Guid.NewGuid(), new Name("Sherlock", "Holmes"), "sherlock@holmes.co.uk")); | |
json = json.Replace("sherlock@holmes.co.uk", "invalid"); | |
var exception = Catch.Exception(() => | |
JsonConvert.DeserializeObject<Customer>(json) | |
); | |
exception.ShouldContainErrorMessage("Email is invalid"); | |
}; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[DebuggerDisplay("{First,nq} {Last,nq}")] | |
public class Name | |
{ | |
public string First { get; } | |
public string Last { get; } | |
public Name(string first, string last) | |
{ | |
if (string.IsNullOrWhiteSpace(first)) throw new Exception("First name is invalid"); | |
if (string.IsNullOrWhiteSpace(last)) throw new Exception("Last name is invalid"); | |
First = first; | |
Last = last; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using ConductOfCode.Domain; | |
using Machine.Specifications; | |
namespace ConductOfCode.Specs | |
{ | |
[Subject(typeof(Name))] | |
public class NameSpecs | |
{ | |
public class Ctor | |
{ | |
It should_create_an_instance_with_valid_state_via_ctor_parameters = () => new Name("Sherlock", "Holmes").ShouldNotBeNull(); | |
It should_validate_the_first_name = () => Catch.Exception(() => new Name("", "Holmes")).ShouldContainErrorMessage("First name is invalid"); | |
It should_validate_the_last_name = () => Catch.Exception(() => new Name("Sherlock", "")).ShouldContainErrorMessage("Last name is invalid"); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment