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("[email protected]")).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("[email protected]")); | |
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 = "[email protected]" } | |
}; | |
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"), "[email protected]"); | |
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("[email protected]")); | |
subject.ShouldNotBeNull(); | |
}; | |
It should_be_able_to_use_conversion_operators = () => | |
{ | |
var subject = new Customer(Guid.NewGuid(), new Name("Sherlock", "Holmes"), "[email protected]"); | |
subject.ShouldNotBeNull(); | |
}; | |
It should_validate__Id__ = () => Catch.Exception(() => new Customer(Guid.Empty, new Name("Sherlock", "Holmes"), "[email protected]")).ShouldContainErrorMessage("Id is invalid"); | |
It should_validate__Name__ = () => Catch.Exception(() => new Customer(Guid.NewGuid(), null, "[email protected]")).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("[email protected]").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("[email protected]"); | |
result.ShouldEqual("[email protected]"); | |
}; | |
It should_convert_string_to_Email = () => | |
{ | |
Email result = "[email protected]"; | |
result.Value.ShouldEqual("[email protected]"); | |
}; | |
} | |
public class Equality | |
{ | |
It should_be_equatable_with_another_Email = () => | |
{ | |
new Email("[email protected]").Equals(new Email("[email protected]")).ShouldBeTrue(); | |
new Email("[email protected]").Equals((object)new Email("[email protected]")).ShouldBeTrue(); | |
new Email("[email protected]").Equals(new Email("[email protected]")).ShouldBeFalse(); | |
new Email("[email protected]").Equals((Email)null).ShouldBeFalse(); | |
}; | |
It should_be_equatable_with_string = () => | |
{ | |
new Email("[email protected]").Equals("[email protected]").ShouldBeTrue(); | |
new Email("[email protected]").Equals((object)"[email protected]").ShouldBeTrue(); | |
new Email("[email protected]").Equals("[email protected]").ShouldBeFalse(); | |
new Email("[email protected]").Equals((string)null).ShouldBeFalse(); | |
}; | |
It should_support_equality_operator = () => | |
{ | |
(new Email("[email protected]") == new Email("[email protected]")).ShouldBeTrue(); | |
(new Email("[email protected]") == "[email protected]").ShouldBeTrue(); | |
(new Email("[email protected]") == new Email("[email protected]")).ShouldBeFalse(); | |
(new Email("[email protected]") == "[email protected]").ShouldBeFalse(); | |
}; | |
It should_support_inequality_operator = () => | |
{ | |
(new Email("[email protected]") != new Email("[email protected]")).ShouldBeFalse(); | |
(new Email("[email protected]") != "[email protected]").ShouldBeFalse(); | |
(new Email("[email protected]") != new Email("[email protected]")).ShouldBeTrue(); | |
(new Email("[email protected]") != "[email protected]").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"), "[email protected]"); | |
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"), "[email protected]")); | |
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"), "[email protected]")); | |
json = json.Replace("[email protected]", "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