Let's say I have a class (simplistic for example) and I want to ensure that the PersonId and Name fields are ALWAYS populated.
public class Person
{
int PersonId { get; set; }
string Name { get; set; }
string Address { get; set; }
}
Currently, my query would be:
Person p = conn.Query<Person>("SELECT * FROM People");
However, I may have changed my database schema from PersonId to PID and now the code is going to go through just fine.
What I'd like to do is one of the following:
Decorate the property PersonId with an attribute such as Required (that dapper can validate)
Tell dapper to figure out that the mappings are not getting filled out completely (i.e. throw an exception when not all the properties in the class are filled out by data from the query).
Is this possible currently? If not, can someone point me to how I could do this without affecting performance too badly?
IMHO, the second option would be the best because it won't break existing code for users and it doesn't require more attribute decoration on classes we may not have access to.
At the moment, no this is not possible. And indeed, there are a lot of cases where it is actively useful to populate a partial model, so I wouldn't want to add anything implicit. In many cases, the domain model is an extended view on the data model, so I don't think option 2 can work - and I know it would break in a gazillion places in my code ;p If we restrict ourselves to the more explicit options...
So far, we have deliberately avoided things like attributes; the idea has been to keep it as lean and direct as possible. I'm not pathologically opposed to attributes - just: it can be problematic having to probe them. But maybe it is time... we could perhaps also allow simple column mapping at the same time, i.e.
[Map(Name = "Person Id", Required = true)]
int PersonId { get; set; }
where both Name and Required are optional. Thoughts? This is problematic in a few ways, though - in particular at the moment we only probe for columns we can see, in particular in the extensibility API.
The other possibility is an interface that we check for, allowing you to manually verify the data after loading; for example:
public class Person : IMapCallback {
void IMapCallback.BeforePopulate() {}
void IMapCallback.AfterPopulate() {
if(PersonId == 0)
throw new InvalidOperationException("PersonId not populated");
}
}
The interface option makes me happier in many ways:
it avoids a lot of extra reflection probing (just one check to do)
it is more flexible - you can choose what is important to you
it doesn't impact the extensibility API
but: it is more manual.
I'm open to input, but I want to make sure we get it right rather than rush in all guns blazing.
Related
Suppose I create a model
public class Foo :TableEntity {
public int OriginalProperty {get;set;}
}
I then deploy a service that periodically updates the values of OriginalProperty with code similar to...
//use model-based query
var query = new TableQuery<Foo>().Where(…);
//get the (one) result
var row= (await table.ExecuteQueryAsync(query)).Single()
//modify and write it back
row.OriginalProperty = some_new_value;
await table.ExecuteAsync(TableOperation.InsertOrReplace(row));
At some later time I decide I want to add a new property to Foo for use by a different service.
public class Foo :TableEntity {
public int OriginalProperty {get;set;}
public int NewProperty {get;set;}
}
I make this change locally and start updating a few records from my local machine without updating the original deployed service.
The behaviour I am seeing is that changes I make to NewProperty from my local machine are lost as soon as the deployed service updates the record. Of course this makes sense in some ways. The service is unaware that NewProperty has been added and has no reason to preserve it. However my understanding was that the TableEntity implementation was dictionary-based so I was hoping that it would 'ignore' (i.e. preserve) newly introduced columns rather than delete them.
Is there a way to configure the query/insertion to get the behaviour I want? I'm aware of DynamicTableEntity but it's unclear whether using this as a base class would result in a change of behaviour for model properties.
Just to be clear, I'm not suggesting that continually fiddling with the model or having multiple client models for the same table is a good habit to get into, but it's definitely useful to be able to occasionally add a column without worrying about redeploying every service that might touch the affected table.
You can use InsertOrMerge instead of InsertOrReplace.
I'm learning DDD approach step by step with imaginary business domain by reading books of Eric Evans and Vaughn Vernon and I try to implement it using in my project using PHP (but it really doesn't matter here).
Recently I've been reading a lot of Aggregate, AggregateRoot and Entity patterns for models that should be defined by a domain. And, frankly, I'm not sure I understand all definitions well so I decided to ask my questions here.
At first I'd like to present my (sub)domain responsible for employees' holidays management which should make answers for my questions easier.
The most trivial case is that the Employee can be found in many Teams. When the employee decides to take few days off, he has to send a HolidaysRequest with metadata like type of holidays (like rest holidays, some days off to take care of his child, etc.), the acceptance status and of course time range when he's not going to appear in his office. Of couse HolidaysRequest should be aware of which Employee has sent the HolidaysRequest. I'd like also to find all HolidaysRequest that are sent by Employee.
I'm quite sure that things like DateRange or HolidayType are pure ValueObjects. It's quite clear for me. The problems start when I have to define boundries of entities. I may have bad practices of defining associations by nesting objects in entities, so, please, tell me finding out the definitions of responsibilities here.
What is an entity here? What should be an Aggregate and where's the place for AggregateRoot?
How to define associations between entities? E.g. an Employee can belong to multiple Teams or HolidaysRequest is authored by Employee and assigned to another Employee who can accept it. Should they be implemented as Aggregates?
Why I'm asking these questions? Because few weeks ago I've posted a question here and one of answers was to think about relations between Employee and Teams, that they should be in the single Aggreate called EmployeeInTeam but I'm not sure I understand it in proper way.
Thanks for any advice.
The main thing about DDD, is to put focus in the domain, that's why its called Domain Driven Design.
When you start asking about relationships, aggregates and entities without even deeply exploring what consists your domain, you're actually looking for database modeling instead of domain.
Please, I'm not saying you're asking wrong questions, nor criticising they, I think you're not wrong at all when trying to put in practice while studying.
I'm not DDD expert, I'm learning just like you, but I'm gonna try to help.
Start by thinking what situation's may arise about Holydays Management. When you have different rules for something, you could start by using strategies (I'm saying is the final solution).
Building a nice and meaningful domain, is very hard (at least for me). You write code. Test it. Have insights, throw your code way and rewrite it. Refactor it. In your software's lifecycle, you should put focus on domain, therefore you should be always improving it.
Start by coding (like a domain's draft) to see how it looks like. Let's exercise it. First of all, why do we need to manage this stuff? What problem are we trying to solve? Ahh, sometimes employees ask some days off, we want to control it. We may approve or not, depending on the reason they want "holyday", and how is our team status. If we decline and they still go home, we'll late decide whether we fire or discount in salary. Enforcing ubiquitous language, let's express in code this problem:
public interface IHolydayStrategy
{
bool CanTakeDaysOff(HolydayRequest request);
}
public class TakeCareOfChildren : IHolydayStrategy
{
public bool CanTakeDaysOff(HolydayRequest request)
{
return IsTotalDaysRequestedUnderLimit(request.Range.TotalDays());
}
public bool IsTotalDaysRequestedUnderLimit(int totalDays)
{
return totalDays < 3;
}
}
public class InjuredEmployee : IHolydayStrategy
{
public bool CanTakeDaysOff(HolydayRequest request)
{
return true;
}
}
public class NeedsToRelax : IHolydayStrategy
{
public bool CanTakeDaysOff(HolydayRequest request)
{
return IsCurrentPercentageOfWorkingEmployeesAcceptable(request.TeamRealSize, request.WorkingEmployees)
|| AreProjectsWithinDeadline(request.Projects);
}
private bool AreProjectsWithinDeadline(IEnumerable<Project> projects)
{
return !projects.Any(p => p.IsDeadlineExceeded());
}
private bool IsCurrentPercentageOfWorkingEmployeesAcceptable(int teamRealSize, int workingEmployees)
{
return workingEmployees / teamRealSize > 0.7d;
}
}
public class Project
{
public bool IsDeadlineExceeded()
{
throw new NotImplementedException();
}
}
public class DateRange
{
public DateTime Start { get; set; }
public DateTime End { get; set; }
public int TotalDays()
{
return End.Subtract(Start).Days;
}
public bool IsBetween(DateTime date)
{
return date > Start && date < End;
}
}
public enum HolydayTypes
{
TakeCareOfChildren,
NeedToRelax,
BankOfHours,
Injured,
NeedToVisitDoctor,
WannaVisitDisney
}
public class HolydayRequest
{
public IEnumerable<Project> Projects { get; internal set; }
public DateRange Range { get; set; }
public HolydayTypes Reason { get; set; }
public int TeamRealSize { get; internal set; }
public int WorkingEmployees { get; internal set; }
}
Here is how I quickly wrote this:
Holydays may be granted or not, depending on the situation and
reason, let's create a IHolydayStrategy.
Created an empty (propertyless) HolydayRequest class.
For each possible reason, let's create a different strategy.
If the reason is to take care of children, they can take days off if
the total days request is under a limit.
If the reason is because the employee has been injured, we have no
choice other than allowing the request.
If the reason is because they need to relax, we check if we have an
acceptable percentage of working employees, or if projects are within
deadline.
As soon as I needed some data in the strategy, I used CTRL + . to
automagically create properties in HolydayRequest.
See how I don't even know how these stuff are going to be stored/mapped? I just wrote code to solve a problem, and get piece of information needed to resolve it.
Obviously this is not the final domain, is just a draft. I might take away this code and rewrite, if needed, no feelings for it yet.
People may think it's useless to create an InjuredEmployee class just to always return true, but the point here is to make use of ubiquitous language, to make things as explicit as possible, anyone would read and understand the same thing: "Well, if we have an injured employee, they are always allowed to take days off, regardless of the team's situation and how many days they need.". One of the problems this concept in DDD solves is the misunderstanding of terms and rules between developers, product owners, domain experts, and other participants.
After this, I would start writing some tests with mock data. I might refactor code.
This "3":
public bool IsTotalDaysRequestedUnderLimit(int totalDays)
{
return totalDays < 3;
}
and this "0.7d":
private bool IsCurrentPercentageOfWorkingEmployeesAcceptable(int teamRealSize, int workingEmployees)
{
return workingEmployees / teamRealSize > 0.7d;
}
are specifications, In my point of view, which shouldn't reside in a strategy. We might apply Specification Pattern to make things decoupled.
After we get to a reasonably initial solution with passed tests, now let's think how should we store it. We might use the final defined classes (such as Team, Project, Employee) here to be mapped by an ORM.
As soon as you started writing your domain, relationships will arise between your entities, that's why I usually don't care at all how the ORM will persist my domain, and what is Aggregate at this point.
See how I didn't create an Employee class yet, even though it sounds very important. That's why we shouldn't start by creating entities and their properties, because it's the exact same thing as creating tables and fields.
Your DDD turns into Database Driven Design that way, we don't want this. Of course, eventually we'll make the Employee, but let's take step by step, create only when you need it. Don't try to start modeling everything at once, predicting all entities you're going to need. Put focus on your problem, and how to solve it.
About your questions, what is entity and what is aggregate, I think you're not asking the definition of them, but whether Employee is considered one or other, considering your domain. You'll eventually answer yourself, as soon as your domain start being revealed by your code. You'll know it when you started developing your Application Layer, which should have the responsibility of loading data and delegating to your domain. What data my domain logic expects, from where do I start querying.
I hope I helped someone.
Currently diving into DDD and i've read most of the big blue book of Eric Evans. Quite interesting so far :)
I've been modeling some aggregates where they hold a collection of entities which expire. I've come up with a generic approach of expressing that:
public class Expirable<T>
{
public T Value { get; protected set; }
public DateTime ValidTill { get; protected set; }
public Expirable(T value, DateTime validTill)
{
Value = value;
ValidTill = validTill;
}
}
I am curious what the best way is to invalidate an Expirable (nullify or omit it when working in a set). So far I've been thinking to do that in the Repository constructor since that's the place where you access the aggregates from and acts as a 'collection'.
I am curious if someone has come up with a solution to tackle this and I would be glad to hear it :) Other approaches are also very welcome.
UPDATE 10-1-2013:
This is not DDD with the CQRS/ES approach from Greg Young. But the approach Evans had, since I just started with the book and the first app. Like Greg Young said, if you have to make good tables, you have to make a few first ;)
There are probably multiple ways to approach this, but I, personally, would solve this using the Specification pattern. Assuming object expiration is a business rule that belongs in the domain, I would have a specification in addition to the class you have written. Here is an example:
public class NotExpiredSpecification
{
public bool IsSatisfiedBy(Expirable<T> expirableValue)
{
//Return true if not expired; otherwise, false.
}
}
Then, when your repositories are returning a list of aggregates or when performing any business actions on a set, this can be utilized to restrict the set to un-expired values which will make your code expressive and keep the business logic within the domain.
To learn more about the Specification pattern, see this paper.
I've added a method to my abstract repository InvalidateExpirable. An example would be the UserRepository where I remove in active user sessions like this: InvalidateExpirable(x => x.Sessions, (user, expiredSession) => user.RemoveSession(expiredSession));.
The signature of InvalidateExpirable looks like this: protected void InvalidateExpirable<TExpirableValue>(Expression<Func<T, IEnumerable<Expirable<TExpirableValue>>>> selector, Action<T, Expirable<TExpirableValue>> remover). The method itself uses reflection to extract the selected property from the selector parameter. That property name is glued in a generic HQL query which will traverse over the set calling the remove lambda. user.RemoveSession will remove the session from the aggregate. This way the I keep the aggregate responsible for it's own data. Also in RemoveSession an domain event is raised for future cases.
See: https://gist.github.com/4484261 for an example
Works quite well sofar, I have to see how it works further down in the application though.
Have been reading up on DDD with CQRS/ES (Greg Young approach) and found a great example on the MSDN site about CQRS/ES: http://msdn.microsoft.com/en-us/library/jj554200.aspx
In this example they use the command message queue to queue a Expire message in the future, which will call the Aggregate at the specified time removing/deactivate the expirable construct from the aggregate.
I've taken a read to the Advantages of message based web services article and am wondering if there is there a recommended style/practice to versioning Restful resources in ServiceStack? The different versions could render different responses or have different input parameters in the Request DTO.
I'm leaning toward a URL type versioning (i.e /v1/movies/{Id}), but I have seen other practices that set the version in the HTTP headers (i.e Content-Type: application/vnd.company.myapp-v2).
I'm hoping a way that works with the metadata page but not so much a requirement as I've noticed simply using folder structure/ namespacing works fine when rendering routes.
For example (this doesn't render right in the metadata page but performs properly if you know the direct route/url)
/v1/movies/{id}
/v1.1/movies/{id}
Code
namespace Samples.Movies.Operations.v1_1
{
[Route("/v1.1/Movies", "GET")]
public class Movies
{
...
}
}
namespace Samples.Movies.Operations.v1
{
[Route("/v1/Movies", "GET")]
public class Movies
{
...
}
}
and corresponding services...
public class MovieService: ServiceBase<Samples.Movies.Operations.v1.Movies>
{
protected override object Run(Samples.Movies.Operations.v1.Movies request)
{
...
}
}
public class MovieService: ServiceBase<Samples.Movies.Operations.v1_1.Movies>
{
protected override object Run(Samples.Movies.Operations.v1_1.Movies request)
{
...
}
}
Try to evolve (not re-implement) existing services
For versioning, you are going to be in for a world of hurt if you try to maintain different static types for different version endpoints. We initially started down this route but as soon as you start to support your first version the development effort to maintain multiple versions of the same service explodes as you will need to either maintain manual mapping of different types which easily leaks out into having to maintain multiple parallel implementations, each coupled to a different versions type - a massive violation of DRY. This is less of an issue for dynamic languages where the same models can easily be re-used by different versions.
Take advantage of built-in versioning in serializers
My recommendation is not to explicitly version but take advantage of the versioning capabilities inside the serialization formats.
E.g: you generally don't need to worry about versioning with JSON clients as the versioning capabilities of the JSON and JSV Serializers are much more resilient.
Enhance your existing services defensively
With XML and DataContract's you can freely add and remove fields without making a breaking change. If you add IExtensibleDataObject to your response DTO's you also have a potential to access data that's not defined on the DTO. My approach to versioning is to program defensively so not to introduce a breaking change, you can verify this is the case with Integration tests using old DTOs. Here are some tips I follow:
Never change the type of an existing property - If you need it to be a different type add another property and use the old/existing one to determine the version
Program defensively realize what properties don't exist with older clients so don't make them mandatory.
Keep a single global namespace (only relevant for XML/SOAP endpoints)
I do this by using the [assembly] attribute in the AssemblyInfo.cs of each of your DTO projects:
[assembly: ContractNamespace("http://schemas.servicestack.net/types",
ClrNamespace = "MyServiceModel.DtoTypes")]
The assembly attribute saves you from manually specifying explicit namespaces on each DTO, i.e:
namespace MyServiceModel.DtoTypes {
[DataContract(Namespace="http://schemas.servicestack.net/types")]
public class Foo { .. }
}
If you want to use a different XML namespace than the default above you need to register it with:
SetConfig(new EndpointHostConfig {
WsdlServiceNamespace = "http://schemas.my.org/types"
});
Embedding Versioning in DTOs
Most of the time, if you program defensively and evolve your services gracefully you wont need to know exactly what version a specific client is using as you can infer it from the data that is populated. But in the rare cases your services needs to tweak the behavior based on the specific version of the client, you can embed version information in your DTOs.
With the first release of your DTOs you publish, you can happily create them without any thought of versioning.
class Foo {
string Name;
}
But maybe for some reason the Form/UI was changed and you no longer wanted the Client to use the ambiguous Name variable and you also wanted to track the specific version the client was using:
class Foo {
Foo() {
Version = 1;
}
int Version;
string Name;
string DisplayName;
int Age;
}
Later it was discussed in a Team meeting, DisplayName wasn't good enough and you should split them out into different fields:
class Foo {
Foo() {
Version = 2;
}
int Version;
string Name;
string DisplayName;
string FirstName;
string LastName;
DateTime? DateOfBirth;
}
So the current state is that you have 3 different client versions out, with existing calls that look like:
v1 Release:
client.Post(new Foo { Name = "Foo Bar" });
v2 Release:
client.Post(new Foo { Name="Bar", DisplayName="Foo Bar", Age=18 });
v3 Release:
client.Post(new Foo { FirstName = "Foo", LastName = "Bar",
DateOfBirth = new DateTime(1994, 01, 01) });
You can continue to handle these different versions in the same implementation (which will be using the latest v3 version of the DTOs) e.g:
class FooService : Service {
public object Post(Foo request) {
//v1:
request.Version == 0
request.Name == "Foo"
request.DisplayName == null
request.Age = 0
request.DateOfBirth = null
//v2:
request.Version == 2
request.Name == null
request.DisplayName == "Foo Bar"
request.Age = 18
request.DateOfBirth = null
//v3:
request.Version == 3
request.Name == null
request.DisplayName == null
request.FirstName == "Foo"
request.LastName == "Bar"
request.Age = 0
request.DateOfBirth = new DateTime(1994, 01, 01)
}
}
Framing the Problem
The API is the part of your system that exposes its expression. It defines the concepts and the semantics of communicating in your domain. The problem comes when you want to change what can be expressed or how it can be expressed.
There can be differences in both the method of expression and what is being expressed. The first problem tends to be differences in tokens (first and last name instead of name). The second problem is expressing different things (the ability to rename oneself).
A long-term versioning solution will need to solve both of these challenges.
Evolving an API
Evolving a service by changing the resource types is a type of implicit versioning. It uses the construction of the object to determine behavior. Its works best when there are only minor changes to the method of expression (like the names). It does not work well for more complex changes to the method of expression or changes to the change of expressiveness. Code tends to be scatter throughout.
Specific Versioning
When changes become more complex it is important to keep the logic for each version separate. Even in mythz example, he segregated the code for each version. However, the code is still mixed together in the same methods. It is very easy for code for the different versions to start collapsing on each other and it is likely to spread out. Getting rid of support for a previous version can be difficult.
Additionally, you will need to keep your old code in sync to any changes in its dependencies. If a database changes, the code supporting the old model will also need to change.
A Better Way
The best way I've found is to tackle the expression problem directly. Each time a new version of the API is released, it will be implemented on top of the new layer. This is generally easy because changes are small.
It really shines in two ways: first all the code to handle the mapping is in one spot so it is easy to understand or remove later and second it doesn't require maintenance as new APIs are developed (the Russian doll model).
The problem is when the new API is less expressive than the old API. This is a problem that will need to be solved no matter what the solution is for keeping the old version around. It just becomes clear that there is a problem and what the solution for that problem is.
The example from mythz's example in this style is:
namespace APIv3 {
class FooService : RestServiceBase<Foo> {
public object OnPost(Foo request) {
var data = repository.getData()
request.FirstName == data.firstName
request.LastName == data.lastName
request.DateOfBirth = data.dateOfBirth
}
}
}
namespace APIv2 {
class FooService : RestServiceBase<Foo> {
public object OnPost(Foo request) {
var v3Request = APIv3.FooService.OnPost(request)
request.DisplayName == v3Request.FirstName + " " + v3Request.LastName
request.Age = (new DateTime() - v3Request.DateOfBirth).years
}
}
}
namespace APIv1 {
class FooService : RestServiceBase<Foo> {
public object OnPost(Foo request) {
var v2Request = APIv2.FooService.OnPost(request)
request.Name == v2Request.DisplayName
}
}
}
Each exposed object is clear. The same mapping code still needs to be written in both styles, but in the separated style, only the mapping relevant to a type needs to be written. There is no need to explicitly map code that doesn't apply (which is just another potential source of error). The dependency of previous APIs is static when you add future APIs or change the dependency of the API layer. For example, if the data source changes then only the most recent API (version 3) needs to change in this style. In the combined style, you would need to code the changes for each of the APIs supported.
One concern in the comments was the addition of types to the code base. This is not a problem because these types are exposed externally. Providing the types explicitly in the code base makes them easy to discover and isolate in testing. It is much better for maintainability to be clear. Another benefit is that this method does not produce additional logic, but only adds additional types.
I am also trying to come with a solution for this and was thinking of doing something like the below. (Based on a lot of Googlling and StackOverflow querying so this is built on the shoulders of many others.)
First up, I don’t want to debate if the version should be in the URI or Request Header. There are pros/cons for both approaches so I think each of us need to use what meets our requirements best.
This is about how to design/architecture the Java Message Objects and the Resource Implementation classes.
So let’s get to it.
I would approach this in two steps. Minor Changes (e.g. 1.0 to 1.1) and Major Changes (e.g 1.1 to 2.0)
Approach for minor changes
So let’s say we go by the same example classes used by #mythz
Initially we have
class Foo { string Name; }
We provide access to this resource as /V1.0/fooresource/{id}
In my use case, I use JAX-RS,
#Path("/{versionid}/fooresource")
public class FooResource {
#GET
#Path( "/{id}" )
public Foo getFoo (#PathParam("versionid") String versionid, (#PathParam("id") String fooId)
{
Foo foo = new Foo();
//setters, load data from persistence, handle business logic etc
Return foo;
}
}
Now let’s say we add 2 additional properties to Foo.
class Foo {
string Name;
string DisplayName;
int Age;
}
What I do at this point is annotate the properties with a #Version annotation
class Foo {
#Version(“V1.0")string Name;
#Version(“V1.1")string DisplayName;
#Version(“V1.1")int Age;
}
Then I have a response filter that will based on the requested version, return back to the user only the properties that match that version. Note that for convenience, if there are properties that should be returned for all versions, then you just don’t annotate it and the filter will return it irrespective of the requested version
This is sort of like a mediation layer. What I have explained is a simplistic version and it can get very complicated but hope you get the idea.
Approach for Major Version
Now this can get quite complicated when there is a lot of changes been done from one version to another. That is when we need to move to 2nd option.
Option 2 is essentially to branch off the codebase and then do the changes on that code base and host both versions on different contexts. At this point we might have to refactor the code base a bit to remove version mediation complexity introduced in Approach one (i.e. make the code cleaner) This might mainly be in the filters.
Note that this is just want I am thinking and haven’t implemented it as yet and wonder if this is a good idea.
Also I was wondering if there are good mediation engines/ESB’s that could do this type of transformation without having to use filters but haven’t seen any that is as simple as using a filter. Maybe I haven’t searched enough.
Interested in knowing thoughts of others and if this solution will address the original question.
Just wanted to get the groups thoughts on how to handle configuration details of entities.
What I'm thinking of specifically is high level settings which might be admin-changed. the sort of thing that you might store in the app or web.config ultimately, but from teh DDD perspective should be set somewhere in the objects explicitly.
For sake of argument, let's take as an example a web-based CMS or blog app.
A given blog Entry entity has any number of instance settings like Author, Content, etc.
But you also might want to set (for example) default Description or Keywords that all entries in the site should start with if they're not changed by the author. Sure, you could just make those constants in the class, but then the site owner couldn't change the defaults.
So my thoughts are as follows:
1) use class-level (static) properties to represent those settings, and then set them when the app starts up, either setting them from the DB or from the web.config.
or
2) use a separate entity for holding the settings, possibly a dictionary, either use it directly or have it be a member of the Entry class
What strikes you all as the most easy / flexible? My concerns abou the first one is that it doesn't strike me as very pluggable (if I end up wanting to add more features) as changing an entity's class methods would make me change the app itself as well (which feels like an OCP violation). The second one feels like it's more heavy, though, especially if I then have to cast or parse values out of a dictionary.
I would say that that whether a value is configurable or not is irrelevant from the Domain Model's perspective - what matters is that is is externally defined.
Let's say that you have a class that must have a Name. If the Name is always required, it must be encapsulated as an invariant irrespective of the source of the value. Here's a C# example:
public class MyClass
{
private string name;
public MyClass(string name)
{
if(name == null)
{
throw new ArgumentNullException("name");
}
this.name = name;
}
public string Name
{
get { return this.name; }
set
{
if(value == null)
{
throw new ArgumentNullException("name");
}
this.name = value;
}
}
}
A class like this effectively protects the invariant: Name must not be null. Domain Models must encapsulate invariants like this without any regard to which consumer will be using them - otherwise, they would not meet the goal of Supple Design.
But you asked about default values. If you have a good default value for Name, then how do you communicate that default value to MyClass.
This is where Factories come in handy. You simply separate the construction of your objects from their implementation. This is often a good idea in any case. Whether you choose an Abstract Factory or Builder implementation is less important, but Abstract Factory is a good default choice.
In the case of MyClass, we could define the IMyClassFactory interface:
public interface IMyClassFactory
{
MyClass Create();
}
Now you can define an implementation that pulls the name from a config file:
public ConfigurationBasedMyClassFactory : IMyClassFactory
{
public MyClass Create()
{
var name = ConfigurationManager.AppSettings["MyName"];
return new MyClass(name);
}
}
Make sure that code that needs instances of MyClass use IMyClassFactory to create it instead of new'ing it up manually.