@yreynhout
Error: Twitter did not respond. Please wait a few minutes and refresh this page.
On CQRS, DDD(D), ES, …
Every so often someone new arrives at the DDD/CQRS list (*) and topics such as set based validation rear their head, resulting in near-endless threads of discussion and coming to a common understanding. A topic that isn’t as often discussed is one of roles in the domain model and how that would work in combination with event sourcing. If you want to read up on roles, Mark Seemann has some great posts on that topic on his blog, albeit in a slightly different context. There’s also this video by Udi Dahan about making roles explicit, which is more akin to what I’ll be touching upon here. Fanatics of whitepapers, might get their brain washed by papers like “Role Interfaces“, “The Role Object Pattern“, “Modeling Roles” or “Mock Roles, not Objects“.
Suppose I’m building a Realtor app that has a finite number of real estate property types (think apartment, villa, house, warehouse, etc…). I could model each type of property as a class/type (**) putting specific behavior on each of them. I’d end up with Apartment, Villa, House and Warehouse as aggregate root entity types (and hence as aggregates). What if I had some common behavior that applies to all of them, where the calling code has a desire to be ignorant of the specific type, i.e. it is interested in the role that a property plays, not the specific type of property it represents. Let’s call that role ‘Property’ for lack of inspiration. I could implement it using a base class, an interface or even a totally separate class as we’ll see in a moment. The calling code could be about adding a set of properties to a listing:
public class AddPropertiesToListingService {
readonly IListingRepository _listingRepository;
readonly IPropertyRepository _propertyRepository;
public AddPropertiesToListingService(IListingRepository listingRepository, IPropertyRepository propertyRepository) {
_listingRepository = listingRepository;
_propertyRepository = propertyRepository;
}
public void AddPropertiesToListing(ListingId listingId, PropertyId[] propertyIds) {
var listing = _listingRepository.Get(listingId);
var properties = propertyIds.Select(propertyId => _propertyRepository.Get(propertyId));
listing.AddProperties(properties);
}
}
If this was modeled using either an interface or base class to denote the role, the repository code (***) would look a bit like this:
public interface IPropertyRepository {
Property Get(Guid id);
}
public class PropertyRepository : IPropertyRepository {
readonly IPropertyFactory _factory;
readonly IEventStreamReader _reader;
public PropertyRepository(IPropertyFactory factory, IEventStreamReader reader) {
_factory = factory;
_reader = reader;
}
public Property Get(Guid id) {
var result = _reader.Read(id);
if(!result.HasValue) {
throw new PropertyNotFoundException(id);
}
var root = _factory.Create(result.Value);
root.Initialize(result.Value.Events);
return root;
}
}
//This could also be a Func<EventStream, Property>
public interface IPropertyFactory {
Property Create(EventStream eventStream);
}
public class EventStreamAnalyzingPropertyFactory : IPropertyFactory {
readonly Dictionary<Type, Func<Property>> _propertyFactories;
public EventStreamAnalyzingPropertyFactory() {
_propertyFactories = new Dictionary<Type, Func<Property>>();
//Assume that each of the aggregate root entities
//has a static Factory method that creates a new instance.
_propertyFactories.Add(typeof(ApartmentRegistered), () => Apartment.Factory());
_propertyFactories.Add(typeof(VillaRegistered), () => Villa.Factory());
_propertyFactories.Add(typeof(HouseRegistered), () => House.Factory());
_propertyFactories.Add(typeof(WarehouseRegistered), () => Warehouse.Factory());
//Remark: Yes, this is an OCP violation.
}
public Property Create(EventStream eventStream) {
Func<Property> propertyFactory;
if(!_propertyFactories.TryGet(eventStream[0].GetType(), out propertyFactory))
throw new PropertyUnknownException(eventStream.Id);
return propertyFactory();
}
}
public interface IEventStreamReader {
Optional<EventStream> Read(Guid id);
}
public interface Optional<T> {
bool HasValue { get; }
T Value { get; }
}
public interface EventStream {
Guid Id { get; }
Int32 ExpectedVersion { get; }
//In a real world implementation this would be streaming,
//i.e. IEnumerable<object>
object[] Events { get; }
}
public class PropertyNotFoundException : Exception {
public PropertyNotFoundException(Guid id) { }
}
public class PropertyUnknownException : Exception {
public PropertyUnknownException(Guid id) { }
}
//As an aside, Property could also be
//turned into an interface to describe
//the role behavior.
public abstract class Property : AggregateRootEntity {
/* Common behavior can be put here */
}
//Similar for the other property types.
public class Villa : Property {
public static readonly Func<Villa> Factory = () => new Villa();
Villa() { /* ... */}
/* Specific behavior can be put here */
}
Notice how the factory is made responsible for analyzing the event stream and deciding which Property type to instantiate based on the type of the first event. It should be obvious that “the type of the first event” is just one of the ways you could come to decision of which Property type to instantiate.
A slightly different scenario is one where you load the stream into a dedicated class, instead of relying on a base class or an interface to fulfill the role.
public class Property {
public static readonly Func<Property> Factory = () => new Property();
Property() {
/* Streams from each of the Property types can
be loaded into this Role class */
Register<ApartmentRegistered>(When);
Register<VillaRegistered>(When);
Register<HouseRegistered>(When);
Register<WarehouseRegistered>(When);
/* Notice how I haven't even brought up what
you could do if these were polymorphic
messages */
}
/* Common role behavior goes here */
}
public class PropertyRepository : IPropertyRepository {
readonly IEventStreamReader _reader;
public PropertyRepository(IEventStreamReader reader) {
_reader = reader;
}
public Property Get(Guid id) {
var result = _reader.Read(id);
if(!result.HasValue) {
throw new PropertyNotFoundException(id);
}
var root = Property.Factory();
root.Initialize(result.Value.Events);
return root;
}
}
This is exactly why you shouldn’t use the type name of an aggregate as a form of stream identification (at least if you want to support this kind of scenario). The repository will just load up the stream, being totally ignorant of what class was used to produce the events in the stream in the first place and happily feed it to the Property class.
Another scenario where this technique could prove to be useful is when your entity goes through a life cycle where each state has very different behavior or behavior needs to be limited as of a certain stage in its life cycle. Of course, this shouldn’t be used as an excuse to NOT model things explicitly.
The most important takeaway is that a stream of events does not need to be loaded into the same class all the time and that roles remain useful within a model backed by event sourcing. Like anything, this should be used with moderation and only if applicable.
(*) I’m reliving a scene with Arnold in Total Recall (1990) as I’m writing this (http://www.youtube.com/watch?feature=player_detailpage&v=WFMLGEHdIjE#t=86s).
(**) Having worked in the Realtor business, I can tell you right off the bat that having a class per real estate property type is going to hurt in the long run, but who am I to judge about the usefulness of this particular model.
(***) Don’t complain if the code doesn’t compile out of the box. I used my C# brain compiler.
When mentioning “object inheritance” most people immediately think of “class inheritance“. Alas, that’s not what it is. Quoting from Streamlined Object Modeling*:
Object inheritance allows two objects representing a single entity to be treated as one object by the rest of the system.
Put a different way, where class inheritance allows for code reuse, object inheritance allows for data reuse.
Where could this be useful? Depending on the domain you are in, I’d say quite a few places. Whenever you replicate data from one object to another object, you should stop and consider if “object inheritance” could be applicable. While object inheritance does impose some constraints, it also saves you from writing reconciliation code to synchronize two or more objects (especially in systems that heavily rely on messaging).
A common example I like to use is the one of a video title and a video tape (**). From a real world point of view, a video tape has both the attributes of the tape itself and the title. Yet, if I modeled this as two separate objects, I run into trouble (a.k.a. synchronization woes). If I copy the title information to the tape upon creation of the tape, and I made an error in say the release date of the title, I now have to replicate that information to the “affected” tapes. Don’t get me wrong, sometimes this kind of behavior is desirable, i.e. it makes sense from a business perspective. But what if it doesn’t? That’s where “object inheritance” comes in. To a large extent, it can cover the scenarios that a synchronization based solution can, IF constraints are met.
The aforementioned book also states other constraints such as the child object exhibiting the parent object’s “profile” (think interface), but I find those less desirable in an environment that uses CQRS. For more in-depth information, I strongly suggest you read it. It has a wealth of information on this very topic.
In its simplest form “object inheritance” looks and feels like object composition.
The composition can be hidden from child object consuming code behind a repository’s facade as shown below.
The gist of “object inheritance” is that the child object (the video tape) asks the parent (the video) for data or invokes side-effect free functions on the parent to accomplish its task.
Whether you go down the route of “object inheritance” or (message based) “synchronization”, you will have to think about object life-cycles (both parent and child). Sometimes it’s desirable for the child to have a still view of the parent, a snapshot if you will. Other times you may want the child to always see the parent in its most current form. Children can even change parent during their life-cycle. Other children may want a more “controlled” view of the parent, rolling forward or backward based on their needs or context. You can get pretty sophisticated with this technique, especially in an event sourced domain model since it’s very well suited to roll up to a certain point in time or a certain revision of a parent. In the same breath, I should also say that you can very well shoot yourself in the foot with this technique, especially if the composition is to be taking place in the user interface, in which case there’s no point in using this. It’s also very easy to bury yourself in the pit of abstraction when talking about “child” and “parent”, so do replace those words with nouns from your domain, and get your archetypes right.
All in all, I’ve found this a good tool in the box, that plays nicely with aggregates, event-sourcing, even state-based models that track the concept of time. It’s not something I use everyday, but whenever I did, I ended up with less code. YMMV.
(*) Streamlined object modeling is a successor to Peter Coad’s “Modeling In Color“. A Kindle version of the former is available.
(**) I’m from the “cassette & video” ages.
Once in a while I hear/read people struggle with projection performance. The root causes of their performance issues are diverse:
Ever heard of the book Refactoring to Patterns? It has a nice refactoring in there, called Move Accumulation to Collecting Parameter that refers to the Collecting Parameter pattern on C2. How would this help with thy projections? Well, what if you could decouple the act of performing an action from collecting what is required to be able to perform an action? Put another way, what if you decouple the act of executing SQL DML statements from collecting those statements during projection (a.k.a. event handling)? So, instead of …
… we add another level of indirection …
The most noticeable differences are the decoupling from persistence technology(*), no reads, and no promises with regard to when the requested operations will be executed/flushed to storage. Usually, the IProjectionSqlOperations interface will have a very small surface (i.e. low member count), covering INSERT, UPDATE and DELETE. During live/production projection building you could have an implementation of this interface (a.k.a. strategy) that flushes as soon as an operation is requested.
However, the more interesting implementations are the ones that are used during rebuild.
This implementation translates the requested operations into sql statement objects (abstracted by ISqlStatement) and pushes them onto something that observes these sql statements. The observer couldn’t care less what the actual sql statements are (that happened in the projection handler above). The simplest observer implementation could look something like this …
Of course, collecting in and by itself is not all that useful. You have to do something with what you’ve collected (“the money in the box”). Let’s look at another observer that takes a slightly different approach.
Without diving too much into the details, this observer flushes statements to the database as soon as a hard-coded threshold is reached. It does so in a batch-like fashion to minimize the number of roundtrips, but still adhering to the limitations that come with this particular ADO.NET data provider. Other implementations use SqlBulkCopy to maximize performance (but come with their own limitations). Depending on what memory resources a server has you could get pretty creative as to which strategy you choose to rebuild a large projection.
I’ve shown you SQL centric projections, but please, do step out of the box. Nothing is stopping you from producing and collecting “HttpStatements” for your favorite key-value store or “FileOperations” for your file-based projections. Nothing is stopping you from making different choices for the producing and consuming side. Nothing is stopping you from doing the “statement” execution in an asynchronous and/or parallel fashion. It’s just a matter of exploring your options and use what works in your environment. Next time I’ll show you how reading fits into all this …
(*): Yeah, yeah, I know, too much abstraction kills kittens …
Over the past couple of weeks I’ve been entrenched in UI development, learning – the good parts of – javascript and using libs like Knockout.js, jQuery and Twitter BootStrap. My relationship with javascript, which started when Netscape Navigator was still cool, has been on and off over the past years. Given its current rise, it seemed like a good idea to get intimate again. Tasked with building the administrative part of an app that has a fair bit of client-side behavior, we set out to build the first version euh … failure. It was monolithic, taking on way too many responsibilities in places you didn’t expect, it had hardly any extensibility points (let alone figuring out where to find them), it was overloaded with technical “look at my neat trick to make it bark” concerns, and – to top it off – it was borderline unreadable. It’s safe to say we weren’t very happy with the result (which would haunt us in the months and years to come). Although, in all fairness, there were some nice gems in there.
So, what were we todo? What any agile team should do … be honest about it and tackle it head on. I started hacking on what I thought “it should be more like” and so did another team member. We sat down as a team, did a code review of the current approach to better understand what the actual problems were, compared it with two more approaches, sollicited feedback, bounced off ideas on how to improve the situation, and settled on a path forward.
It’s this path forward I want to show you a glimpse of. This is a more in depth exploration of what I touched upon in viewmodels like you meant it, but in a slightly different context.
If you want to prevent logic from creeping into your view, if you want to clearly define how visual state relates to a piece of data or to a trigger, you should refrain from using primitive types in your viewmodel, or you’ll end up with something similar to the sample below.
Preventing conditions from creeping into views is easy to solve. You just move the condition into the viewmodel, giving it a descriptive name along the way, maybe using a computed observable if it happens to be dynamic. Easy enough and nothing wrong with doing so (in fact I encourage you to do so). What may be less obvious is how to get rid of the primitives. The answer is simple: building the right abstractions.
Being valid or invalid, having focus, visibility, being enabled or disabled, having a value, change tracking and – in the case of data-binding – being able to pause and dispose subscriptions are all concerns that revolve around that one value most people seem to put as a primitive in their viewmodel. Don’t get me wrong, I’m not suggesting that each and every primitive value requires all these concerns, but I’d rather put them in my abstraction than polluting my viewmodel when I do need them.
Next to TextInput and NumericInput you can imagine DateInput, TimeInput, SelectOneInput, SelectManyInput, but also more domain specific inputs like PercentageInput (constrained numeric input), MinDefaultMaxDurationsInput (composite input), or even HtmlColorInput (formatted text input).
What applies to inputs also applies to triggers, which are most commonly visualized as buttons and links. A trigger could be used to replace the CanSave observable and Save function in the first example.
It’s nice to build and have these abstractions, it’s even better to actually use them. Do realize that, like any abstraction, they emerged from actual viewmodel implementations. For those that think “this is a controls library”, I can only feel pitty, because this has little to do with view specific controls.
This was one of the most fundamental insights we had during this journey: the ability to compose viewmodels using the above building blocks and other viewmodels (that in turn have been composed using those building blocks) made them easier to define and reason about. It also helps to keep them at the same level of abstraction.
This line of thought can also be applied at other levels. It has a very recursive and composite feel to it.
Something that’s been missing from the previous snippets is any form of actual behavior. How is one going to introduce logic/flow and interact with dialogs and server side resources? I tend to associate that coordination task with a controller. “Viewmodels as controllers” starts to crumble really fast when you’ve applied the composition above. You’ll end up with big fat viewmodels that feel like a blatant SRP violation.
Therefor making both the controller and how it’s bound to viewmodels explicit, pays off in readability, testability, and keeping the viewmodels thin. Don’t get too hung up on the details below, there are many ways to implement these concerns.
Below is a glimpse of what the final binding could look like.
This is still very much a WIP, but with what little abstractions we’ve come up with, we’ve been able to untangle most of the mess we were in before. Dependencies are now easy to replace (adhering to SRP pays off). Composition has simplified the amount of detail we have to worry about at each level of abstraction. Making the controller explicit has kept our viewmodels free of unnecessary dependencies. All in all we’ve been much more productive using this approach.
A question that comes up from time to time is what role value objects can play in an event sourced domain model and how they contribute to and interact with the events produced by said domain model. Value objects come in many shapes and sizes, but obvious ones like money (amount and currency) and period (range of time values) come to mind. Fields that change together or are used together in the ubiquitous language are prime suspects of information “clustering” and can usually be represented as value objects when they clearly lack any form of identity. They are an ideal place to DRY up some of the validation, security and contextual capturing code that surrounds these fields.
Below you’ll find a stripped version of a value object from my own domain. Things you can’t easily spot from the code below is the clusivity of the lower and upper boundary of this range-like value type, partially because I omitted the arithmetic operations. The finer details, such as which methods are required and who to collaborate with, surface as you discuss them with domain experts.
namespace Domain {
public class RolloutPeriod {
readonly DateTime _from;
readonly DateTime _to;
public RolloutPeriod(DateTime from, DateTime to) {
if(from > to)
throw new ArgumentException("The from value of the rollout period must be less than or equal to the to value.", "from");
_from = from;
_to = to;
}
//Arithmetic members omitted for brevity
public bool Equals(RolloutPeriod other) {
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return other._from.Equals(_from) && other._to.Equals(_to);
}
public override bool Equals(object obj) {
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof (RolloutPeriod)) return false;
return Equals((RolloutPeriod) obj);
}
public override int GetHashCode() {
return _from.GetHashCode() ^ _to.GetHashCode();
}
}
}
Something you do notice in the above code is that there are no getters nor setters. This is deliberate. On one hand, it allows me to redefine how I represent values on the inside (i.e. the from and to value). On the other hand, I decouple any consuming code from the internals and gently steer that code into a Tell Don’t Ask style of interaction. It makes you think about the question: “Why do you want access to my internals? What is it that you want to achieve with them? Just tell me what you want to do.”.
Once you get that right, you bump into the next hurdle: using value objects in an event sourced domain model.
namespace Domain {
public class Schedule : AggregateRootEntity {
public void Rollout(RolloutPeriod period) {
//Guards ommitted for brevity
ApplyEvent(Build.RolledoutSchedule.ForPeriod(period));
}
}
public abstract class AggregateRootEntity {
Guid _id;
long _version;
protected void ApplyEvent(IEventBuilder<IEvent> builder) {
var @event = builder.Build(_id, _version++);
//The usual stuff as found below goes here
//https://github.com/gregoryyoung/m-r/blob/master/SimpleCQRS/Domain.cs
}
}
}
namespace Messaging {
public static class Build {
public static RolledoutScheduleBuilder RolledoutSchedule { get { return new RolledoutScheduleBuilder(); } }
}
public class RolledoutScheduleBuilder : IEventBuilder<RolledoutScheduleEvent> {
RolloutPeriod _rolloutPeriod;
public RolledoutScheduleEvent Build(Guid id, long version) {
return new RolledoutScheduleEvent(id, version, _rolloutPeriod);
}
public RolledoutScheduleBuilder ForPeriod(RolloutPeriodBuilder builder) {
_rolloutPeriod = builder.Build();
return this;
}
}
public interface IEventBuilder<out TEvent> where TEvent : IEvent {
TEvent Build(Guid id, long version);
}
public class RolledoutScheduleEvent : IEvent {
public RolledoutScheduleEvent(Guid id, long version, RolloutPeriod period) {
Id = id;
Version = version;
Period = period;
}
public RolloutPeriod Period { get; private set; }
public long Version { get; private set; }
public Guid Id { get; private set; }
}
public interface IEvent { }
public class RolloutPeriod {
public DateTime From { get; private set; }
public DateTime To { get; private set; }
public RolloutPeriod(DateTime from, DateTime to) {
From = from;
To = to;
}
}
public class RolloutPeriodBuilder {
DateTime _from;
DateTime _to;
public RolloutPeriodBuilder From(DateTime value) {
_from = value;
return this;
}
public RolloutPeriodBuilder To(DateTime value) {
_to = value;
return this;
}
public RolloutPeriod Build() {
return new RolloutPeriod(_from, _to);
}
}
}
The rollout period passed into the Schedule rollout method, is a value object part of the domain model. The rollout period of the event – a bit obfuscated by the syntax ‘Build.RolledoutSchedule.ForPeriod’ above – is part of the messaging bits. How do these two meet without exposing internals?
One thing I’d like to make clear: the domain value object IS NOT the data structure used inside the event. It’s a different type.
By applying double dispatch to the domain rollout period and adding a builder extension method – inside the domain model – that is domain rollout period aware, we get a nice translation going.
namespace Domain {
public class RolloutPeriod {
//... see above for other members
internal void BuildValue(RolloutPeriodBuilder builder) {
builder.From(_from).To(_to);
}
}
public static class BuildExtensions {
public static RolledoutScheduleBuilder ForPeriod(this RolledoutScheduleBuilder builder, RolloutPeriod period) {
var valueBuilder = new RolloutPeriodBuilder();
period.BuildValue(valueBuilder);
builder.ForPeriod(valueBuilder);
return builder;
}
}
}
The reverse, going from an event (or data structure within that event) to a value object, is just as easy.
namespace Domain {
public class RolloutPeriod {
//... see above for other members
internal static RolloutPeriod FromEvent(Messaging.RolloutPeriod period) {
return new RolloutPeriod(period.From, period.To);
}
}
public class Schedule : AggregateRootEntity {
void Apply(RolledOutScheduleEvent @event) {
_rolloutPeriod = RolloutPeriod.FromEvent(@event.RolloutPeriod);
//...
}
}
}
So, really, there’s no excuse to NOT use value objects inside an event sourced domain model nor to expose the internal state of those value objects for event sourcing purposes. Happy coding …
P.S. More information on event builders can be found here.
I promised to give you a taste of temporal C# method analysis. You can find a starting point here for git repositories: https://github.com/yreynhout/sharptemporalgitalyzer.
Most agile processes are about (shortening) feedback loops. They come in many shapes and sizes: red/green/refactor, burn down charts, continuous integration, ui mockups, quality assurance … to name a few. One I recently rediscovered is the power of queries on a version control system.
If history has taught me anything, it’s that learning from the past is essential to understand the future better. A GodFather inspired quote.
While VCS (version control system) statistics are not a new thing and countless tools exist to query the lines of code in a file, in general I find them to be just that … too general. Nobody knows the specifics and structure of your working copy as well as you do (or maybe you don’t if you got thrown into it, but that’s another story).
I’m using the term working copy layout to refer to the structure of code within a specific branch. The term repository layout usually refers to how your branches are structured. Keep this in mind while reading further.
Perhaps you’ve aligned your working copy layout – like so many have done before us – along the lines of layers or tiers of a project.
Needless to say, this list is endless. Both your working copy and VCS are a goldmine waiting to be exploited. The good news is that you don’t need a whole lot of tooling to get started. Pretty much any language has an API that let’s you deal with files and folders, so that should cover the working copy part. Most modern version control systems have several language bindings that allow you to automate them. And even if they don’t, there’s probably a command line tool you can invoke to get the data you want. So far I tried this out on Git, Mercurial and Subversion without any noticeable problems.
If you want to do any meaningful analysis, you should – and I can’t stress this enough – start by figuring out which question(s) you want answered. Some might be answered by merely exporting the log out of the VCS, others by scanning the files and folders in your working copy. The difficult ones – the ones that go beyond the file name/size into the file content – might require some script, regular expressions or language parsers to be able to tell you things such as how your code is changing (Michael Feathers has some nice things to say about that). Still others might be answered by correlating log messages to your bug/issue/story tracking tool to certain clusters of code. For simple log related questions I found that map/reduce over a canonical log model can do wonders.
I’ve been able to perform the method-level analysis mentioned by Michael Feathers – using NRefactory – to get an idea of how methods evolved in a C# project over time. I’ll explain that approach in an upcoming post.
I’ll show you a brief example of how you can plot a correlation between changes to files and large files. The first query gives me an idea of the most changed files. It’s using SharpSvn to get at each revision. You could use Mercurial.NET or libgit2sharp to obtain similar results in Mercurial or Git.
from mapped in
(from log in logs
from path in log.ChangedPaths
where
path.Action == SvnChangeAction.Add ||
path.Action == SvnChangeAction.Modify ||
path.Action == SvnChangeAction.Replace
select new {
Path = path.RepositoryPath,
CommitCount = 1
})
group mapped by mapped.Path
into reduced
select new MostChangedPath {
RepositoryPath = reduced.Key,
CommitCount = reduced.Sum(i => i.CommitCount)
};
Next up is a query that gives me an idea of the line count on a file by file basis. The GetFiles function recursively iterates all files in a working copy that match a given search pattern regular expression (to match multiple files).
GetFiles(workingCopyPath, GetSearchPatternExpression(), SearchOption.AllDirectories).
Select(filePath => new FileLineCount {
FilePath = filePath,
LineCount = File.ReadAllLines(filePath).Length
});
Combined with some charting and unleashed on the .cs files of the popular Json.NET library, you’ll get something that resembles the diagram below. Yeah, it’s gonna take some zooming …

Files you don’t have control over should be excluded (or replaced by something lighter if relevant) from the analysis. Big files that change slowly, only become a priority when you’ve dealt with big and smaller files that change frequently (in that order).
Basically all this log and repository analysis revolves around very simple recipes. Get the log entries, run some map/reduce queries over them, evaluate the results. If the metric doesn’t buy you much, try another query. Some VCS are slow with regard to feeding your code the log entries, so you might benefit from caching them in a canonical format on disk or in some map/reduce optimized database system. If you’re using a VCS that is “slow” or “difficult” to work with in this setting, you may want to convert your repository – for analysis purposes only (TFS comes to mind) – to a VCS that is more suitable.
As usual, I’ve only scratched the surface of what is possible. People working with legacy code will surely appreciate this new tool in their box and put it to good use. So, please, do try this at home and at work
. I’m looking forward to hearing back from you. Without wanting to sound patronizing, I do have a few remarks:
Continuing my journey, I figured it shouldn’t be that hard to apply “Your EventStream is a linked list” using Windows Azure Storage Services.
Disclaimer: Again, this is just a proof of concept, a way to get started, not production code … obviously. I’m assuming you know your way around Windows Azure (how to set up an account, stuff like that) and how the .NET SDK works.
Just like with Amazon Web Services, the first thing to create are the containers that will harbor the changesets and aggregate heads. Unlike the AWS example, I’ve chosen to store both using the same service, Windows Azure Blob Storage. Why? Because it offers optimistic concurrency by leveraging ETags. The only unfortunate side-effect is that it seems to force me to break CQS, but I can live with that for now. The Windows Azure Blob Storage provides convenience methods to only create the containers when they don’t exist.
//Setting up the Windows Azure Blob Storage container to store changesets and aggregate heads
const string PrimaryAccessKey = "your-key-here-for-testing-purposes-only-ofcourse-;-)";
const string AccountName = "youraccountname";
const string ChangesetContainerName = "changesets";
const string AggregateContainerName = "aggregates";
var storageAccount =
new CloudStorageAccount(
new StorageCredentialsAccountAndKey(
AccountName,
PrimaryAccessKey),
false);
var blobClient = storageAccount.CreateCloudBlobClient();
var changesetContainer = blobClient.GetContainerReference(ChangesetContainerName);
changesetContainer.CreateIfNotExist();
var aggregateContainer = blobClient.GetContainerReference(AggregateContainerName);
aggregateContainer.CreateIfNotExist();
Now that we’ve set up the infrastructure, let’s tackle the scenario of storing a changeset. The flow is pretty simple. First we try to store the changeset in the changeset container as a blob.
public class ChangesetDocument {
public const long InitialVersion = 0;
//Exposing internals to make the sample easier
public Guid ChangesetId { get; set; }
public Guid? ParentChangesetId { get; set; }
public Guid AggregateId { get; set; }
public long AggregateVersion { get; set; }
public string AggregateETag { get; set; }
public byte[] Content { get; set; }
}
//Assuming there's a changeset document we want to store,
//going by the variable name 'document'.
var changesetBlob = changesetContainer.GetBlobReference(document.ChangesetId.ToString());
var changesetUploadOptions = new BlobRequestOptions {
AccessCondition = AccessCondition.None,
BlobListingDetails = BlobListingDetails.None,
CopySourceAccessCondition = AccessCondition.None,
DeleteSnapshotsOption = DeleteSnapshotsOption.None,
RetryPolicy = RetryPolicies.NoRetry(),
Timeout = TimeSpan.FromSeconds(90),
UseFlatBlobListing = false
};
changesetBlob.UploadByteArray(document.Content, changesetUploadOptions);
const string AggregateIdMetaName = "aggregateid";
const string AggregateVersionMetaName = "aggregateversion";
const string ChangesetIdMetaName = "changesetid";
const string ParentChangesetIdMetaName = "parentchangesetid";
//Set the meta-data of the changeset
//Notice how this doesn't need to be transactional
changesetBlob.Metadata[AggregateIdMetaName] = document.AggregateId.ToString();
changesetBlob.Metadata[AggregateVersionMetaName] = document.AggregateVersion.ToString();
changesetBlob.Metadata[ChangesetIdMetaName] = document.ChangesetId.ToString();
if(document.ParentChangesetId.HasValue)
changesetBlob.Metadata[ParentChangesetIdMetaName] = document.ParentChangesetId.Value.ToString();
changesetBlob.SetMetadata();
If that goes well, we try to upsert the head of the aggregate to get it to point to this changeset. Below, we’re using the ETag of the aggregate head blob as a way of doing optimistic concurrency checking. It caters for both the initial (competing inserters) and update (competing updaters) concurrency.
public static class ExtensionsForChangeDocument {
public static AccessCondition ToAccessCondition(this ChangesetDocument document) {
if(document.AggregateVersion == ChangesetDocument.InitialVersion) {
return AccessCondition.IfNoneMatch("*");
}
return AccessCondition.IfMatch(document.AggregateETag);
}
}
//Upsert the aggregate
var aggregateBlob = aggregateContainer.GetBlobReference(document.AggregateId.ToString());
var aggregateUploadOptions = new BlobRequestOptions {
AccessCondition = document.ToAccessCondition(),
BlobListingDetails = BlobListingDetails.None,
CopySourceAccessCondition = AccessCondition.None,
DeleteSnapshotsOption = DeleteSnapshotsOption.None,
RetryPolicy = RetryPolicies.NoRetry(),
Timeout = TimeSpan.FromSeconds(90),
UseFlatBlobListing = false
};
aggregateBlob.UploadByteArray(document.ChangesetId.ToByteArray(), aggregateUploadOptions);
//Here's where we are breaking CQS if we'd like to cache the aggregate.
//This won't be a problem if we're re-reading the aggregate upon each behavior.
var eTag = aggregateBlob.Properties.ETag;
If the UploadByteArray operation throws a StorageClientException indicating that “The condition specified using HTTP conditional header(s) is not met.”, we know there was some form of optimistic concurrency. In such a case, it’s best to repeat the entire operation.
Now that we’ve dealt with writing, let’s take a look at reading. First we need to fetch the pointer to the last stored and approved changeset identifier.
var aggregateBlob = aggregateContainer.GetBlobReference(aggregateId.ToString());
var aggregateDownloadOptions = new BlobRequestOptions {
AccessCondition = AccessCondition.None,
BlobListingDetails = BlobListingDetails.None,
CopySourceAccessCondition = AccessCondition.None,
DeleteSnapshotsOption = DeleteSnapshotsOption.None,
RetryPolicy = RetryPolicies.NoRetry(),
Timeout = TimeSpan.FromSeconds(90),
UseFlatBlobListing = false
};
var changesetId = new Guid?(new Guid(aggregateBlob.DownloadByteArray(aggregateDownloadOptions)));
var eTag = aggregateBlob.Properties.ETag;
Now that we’ve bootstrapped the reading process, we keep reading each changeset, until there’s no more changeset to read. Each approved changeset contains metadata that points to the previous approved changeset. It’s the responsibility of the calling code todo something useful with the read changesets (e.g. deserialize the content and replay each embedded event into the corresponding aggregate).
while(changesetId.HasValue) {
var changesetBlob = changesetContainer.GetBlobReference(changesetId.Value.ToString());
var changesetDownloadOptions = new BlobRequestOptions {
AccessCondition = AccessCondition.None,
BlobListingDetails = BlobListingDetails.None,
CopySourceAccessCondition = AccessCondition.None,
DeleteSnapshotsOption = DeleteSnapshotsOption.None,
RetryPolicy = RetryPolicies.NoRetry(),
Timeout = TimeSpan.FromSeconds(90),
UseFlatBlobListing = false
};
var content = changesetBlob.DownloadByteArray(changesetDownloadOptions);
changesetBlob.FetchAttributes();
var document = new ChangesetDocument {
AggregateETag = eTag,
AggregateId = new Guid(changesetBlob.Metadata[AggregateIdMetaName]),
AggregateVersion = Convert.ToInt64(changesetBlob.Metadata[AggregateVersionMetaName]),
ChangesetId = new Guid(changesetBlob.Metadata[ChangesetIdMetaName]),
Content = content,
};
if (changesetBlob.Metadata[ParentChangesetIdMetaName] != null)
document.ParentChangesetId = new Guid(changesetBlob.Metadata[ParentChangesetIdMetaName]);
yield return document;
changesetId = document.ParentChangesetId;
}
The only *weird* thing with this code is that I’m propagating the aggregate’s head ETag using the changesets. It’s a modeling issue I’ll have to revisit
.
But that’s basically all there’s to it. In reality, you’ll need a lot more metadata and error handling to make this a success. I should point out that the performance of this service consumed on premise was better than what I experienced with AWS.
Using Windows Azure Storage Services is not so different from the Amazon Web Services in this case. However, this overall technique suffers from a few drawbacks. As mentioned before, upon concurrency, you might be wasting some storage space. Another drawback is the fact that you need to read all changesets that make up the history of an aggregate (or eventsource if want to decouple it from DDD terminology) before being able to apply the first event. There’s ways around this, such as storing all the changeset identifiers in the aggregate head if the total number of behaviors in an aggregate is low on average. You could even partition the aggregate head into multiple documents using nothing but its version number or count of applied behaviors to partition, but that’s the subject of a future exploration. I apologize if this post is a somewhat copy-paste of its AWS counterpart, but given the goal and similarities that was to be expected
.
My post on “Your EventStream is a linked list” might have been somewhat abstract (a more prosaic version can be found here). By way of testing my theory, I set out to apply it using Amazon’s Web Services (AWS) stack. I used AWS DynamoDB as the transactional medium that would handle the optimistic concurrency, and AWS S3 to store the changesets.
Disclaimer: This is just a proof of concept, a way to get started. It’s not production code … obviously
I’m assuming you know your way around AWS (how to set up an account, stuff like that) and how the .NET SDK works.
The first thing todo is to create the container for the changesets. In AWS S3 those things are called buckets. This is a one time operation. You might want to make this conditional at the startup of your application/worker role/what-have-you. The AWS S3 API provides mechanisms to query for buckets, so you should be able to pull that off. On the other hand, when creating buckets using the REST API the HTTP PUT verb is used, and we all know that PUT is supposed to be idempotent. You might want to read up on the PUT Bucket API. One last thing: bucket names are unique across all of S3.
//Setting up the Amazon S3 bucket to store changesets in
const string ChangesetBucketName = "yourorganization_yourboundedcontextname_changesets";
var s3Client = AWSClientFactory.CreateAmazonS3Client();
s3Client.PutBucket(
new PutBucketRequest().
WithBucketName(ChangesetBucketName).
WithBucketRegion(S3Region.EU)); //Your region might vary
The same thing needs to happen in AWS DynamoDB. There, the containers are called tables. Again, this is a one time, conditional operation. You might wanna read up on the specifics of table naming and uniqueness. The throughput provisioning is not something you want to be static. Monitor the load on your system (they have notification events/alerts for that), both in terms of number of reads/writes and bytes used for storage, and use that information to tune the table throughput settings.
//Setting up the Amazon DynamoDB table to store aggregates in
const string AggregateTableName = "yourboundedcontextname_aggregates";
const string AggregateIdName = "aggregate-id";
//At the time of writing, DynamoDBClient didn't make it yet
//into the AWSClientFactory.
var dynamoDbClient = new AmazonDynamoDBClient();
dynamoDbClient.CreateTable(
new CreateTableRequest().
WithTableName(AggregateTableName).
WithKeySchema(
new KeySchema().
WithHashKeyElement(
new KeySchemaElement().
WithAttributeName(AggregateIdName).
WithAttributeType("S")
).
WithRangeKeyElement(null)
).
WithProvisionedThroughput(
new ProvisionedThroughput().
WithReadCapacityUnits(300).
WithWriteCapacityUnits(500)
)
);
Now that we’ve set up the infrastructure, let’s tackle the scenario of storing a changeset. The flow is pretty simple. First we try to store the changeset in AWS S3 as an object in the configured bucket.
//An internal representation of the changeset
public class ChangesetDocument {
public const long InitialValue = 0;
//Exposing internal state here to
//simplify the example.
public Guid AggregateId { get; set; }
public long AggregateVersion { get; set; }
public Guid ChangesetId { get; set; }
public Guid? ParentChangesetId { get; set; }
public byte[] Content { get; set; }
public Stream GetContentStream() {
return new MemoryStream(Content, writable: false);
}
}
//Assuming there's a changeset document we want to store,
//going by the variable name 'document'.
const string AggregateIdMetaName = "x-amz-meta-aggregate-id";
const string AggregateVersionMetaName = "x-amz-meta-aggregate-version";
const string ChangesetIdMetaName = "x-amz-meta-changeset-id";
const string ParentChangesetIdMetaName = "x-amz-meta-parent-changeset-id";
var putObjectRequest = new PutObjectRequest().
WithBucketName(ChangesetBucketName).
WithGenerateChecksum(true).
WithKey(document.ChangesetId.ToString()).
WithMetaData(ChangesetIdMetaName, document.ChangesetId.ToString()).
WithMetaData(AggregateIdMetaName, document.AggregateId.ToString()).
WithMetaData(AggregateVersionMetaName, Convert.ToString(document.AggregateVersion));
if (document.ParentChangesetId.HasValue) {
putObjectRequest.WithMetaData(ParentChangesetIdMetaName, document.ParentChangesetId.Value.ToString());
}
putObjectRequest.WithInputStream(document.GetContentStream());
s3Client.PutObject(putObjectRequest);
If that goes well, we try to create an item in the configured AWS DynamoDB table. The expected values below are the AWS DynamoDB way of doing optimistic concurrency checking. They cater for both the initial (competing inserters) and update (competing updaters) concurrency.
In this example I’m using an incrementing version number to do that. Strictly speaking, I could be using the changeset identifier for that, but an incrementing version number is easier to relate to when you come from an ORM/database background.
const string AggregateVersionName = "aggregate-version";
const string ChangesetIdName = "changeset-id";
public static class ExtensionsForChangesetDocument {
public static KeyValuePair<string, ExpectedAttributeValue>[] ToExpectedValues(this ChangesetDocument document) {
var dictionary = new Dictionary<string, ExpectedAttributeValue>();
if(document.AggregateVersion == ChangesetDocument.InitialValue) {
//Make sure we're the first to create the aggregate
dictionary.Add(AggregateIdName,
new ExpectedAttributeValue().
WithExists(false)
);
} else {
//Make sure nobody changed the aggregate behind our back
dictionary.Add(AggregateIdName,
new ExpectedAttributeValue().
WithExists(true).
WithValue(
new AttributeValue().
WithS(document.AggregateId.ToString()))
);
dictionary.Add(AggregateVersionName,
new ExpectedAttributeValue().
WithValue(
new AttributeValue().
WithN(Convert.ToString(document.AggregateVersion - 1)))
);
}
return dictionary.ToArray();
}
public static KeyValuePair<string, AttributeValue>[] ToItemValues(this ChangesetDocument document) {
//The relevant values to store in the item.
var dictionary = new Dictionary<string, AttributeValue> {
{AggregateIdName, new AttributeValue().WithS(document.AggregateId.ToString())},
{AggregateVersionName, new AttributeValue().WithN(Convert.ToString(document.AggregateVersion))},
{ChangesetIdName, new AttributeValue().WithS(document.ChangesetId.ToString())},
};
return dictionary.ToArray();
}
}
dynamoDbClient.PutItem(
new PutItemRequest().
WithTableName(AggregateTableName).
WithExpected(document.ToExpectedValues()).
WithItem(document.ToItemValues())
);
If the PutItem operation throws an exception indicating that the “ConditionalCheckFailed”, we know there was some form of optimistic concurrency. In such a case, it’s best to repeat the entire operation.
I’ve left duplicate command processing elimination as an exercise for the reader
![]()
The only thing left todo is showing the reverse operation, reading. First we need to fetch the pointer to the last stored and approved changeset identifier. I hope you do realize that a consistent read is not strictly necessary, since the likelihood of concurrency should be low.
var getItemResponse = dynamoDbClient.GetItem(
new GetItemRequest().
WithTableName(AggregateTableName).
WithKey(
new Key().
WithHashKeyElement(
new AttributeValue().
WithS(aggregateId.ToString())).
WithRangeKeyElement(null)).
WithConsistentRead(false).
WithAttributesToGet(ChangesetIdName)
);
Now that we’ve bootstrapped the reading process, we keep reading each changeset, until there’s no more changeset to read. Each approved changeset contains metadata that points to the previous approved changeset. It’s the responsibility of the calling code todo something useful with the read changesets (e.g. deserialize the content and replay each embedded event into the corresponding aggregate).
var changesetId = new Guid?(new Guid(getItemResponse.GetItemResult.Item[ChangesetIdName].S));
while(changesetId.HasValue) {
var getObjectResponse = s3Client.GetObject(
new GetObjectRequest().
WithBucketName(ChangesetBucketName).
WithKey(changesetId.Value.ToString()));
var document = new ChangesetDocument {
AggregateId = new Guid(getObjectResponse.Metadata[AggregateIdMetaName]),
AggregateVersion = Convert.ToInt64(getObjectResponse.Metadata[AggregateVersionMetaName]),
ChangesetId = new Guid(getObjectResponse.Metadata[ChangesetIdMetaName]),
Content = getObjectResponse.ResponseStream.ToByteArray()
};
var parentChangesetIdAsString = getObjectResponse.Metadata[ParentChangesetIdMetaName];
if(parentChangesetIdAsString != null) {
document.ParentChangesetId = new Guid(parentChangesetIdAsString);
}
changesetId = document.ParentChangesetId;
yield return document;
}
And that’s basically all there’s to it. In reality, you’ll need a lot more metadata and error handling to make this a success. I should point out that the performance of these services consumed on premise was abominal. I’m assuming (hoping) that if you consume them from an AWS EC2 instance, performance will be much better.
A clever reader will notice that this technique can easily be transposed to other document/data stores. The specifics will vary, but the theme will be the same. The reason for applying this technique has mainly todo with the inherent constraints of certain datastores. The transactional ones – especially in cloudy environments – are limited with regard to the number of bytes they can store, which makes them less desirable for storing payloads in (prediction of payload size can be hard at times). There are easy ways to side-step concurrency problems such as serializing all aggregate access to one worker/queue, but that’s another discussion.
Although a lot has been written on the topic of viewmodels – both in the MVVM(*) and MVC UI pattern space – it strikes me as odd how underused they are. Why muck around with resource files and attribute based validation hints inside your views when you could provide them with a rich viewmodel? Most people tend to think about their viewmodels in terms of input, either directly (e.g. the value of a textbox) or indirectly (e.g. one of the values you choose from a select, or a list from which to select a row). Why isn’t output equally as important? Why are those magic strings in the view not part of the viewmodel? Why don’t people question the implicit routing and navigation that goes into their views?
Not including output in a view model and primitive obsession are the main reasons why logic and other dependencies creep into your view.
Let’s take the screen of my post on statecharts as an example.

What most people end up with – if they even put in the effort to craft an explicit view model – is something along these lines.
public class TrainStationModel {
public string FilteredBy { get; set; }
public List TrainStations { get; set; }
public TrainStationDetails SelectedTrainStation { get; set; }
}
public class TrainStationItem {
public Guid TrainStationId { get; set; }
public string Name { get; set; }
public string Abbreviation { get; set; }
}
public class TrainStationDetails {
public Guid TrainStationId { get; set; }
[Required, StringLength(80, MinLength = 1)]
public string Name { get; set; }
[Required, StringLength(40, MinLength = 1)]
public string Abbreviation { get; set; }
[Required, Range(0,360)]
public double Longitude { get; set; }
[Required, Range(0,360)]
public double Latitude { get; set; }
}
If we’d want to I18N this screen it would require either a view per culture or a culture+resource aware view. For client-side validation it requires reflective helpers to get the meta-data, sprinkled as attributes on those model properties. Routing and navigation creep into the view as behaviors need to be executed.
Contrast this with what I’ve been using as viewmodels – below – for the past 7 years.
public class TrainStationAdministrationModel : Page {
readonly TextOutputField _header;
readonly TrainStationBodyModel _body;
public TrainStationAdministrationModel() : base("trainstationadministration") {
_header = new TextOutputField("header",
new Text(new StringResource("EurostarStationsKey")));
_body = new TrainStationBodyModel("body");
}
public TextOutputField Header {
get { return _header; }
}
public TrainStationBodyModel Body {
get { return _body; }
}
}
public class TrainStationBodyModel : ModelItem {
readonly Trigger<TextInputField> _filterByTrigger;
readonly TrainStationDetailModel _detail;
readonly SelectList<TrainStationListItem> _list;
readonly Trigger<TextOutputField> _newTrigger;
public TrainStationBodyModel(string id) : base(id) {
_detail = new TrainStationDetailModel("detail");
_list = new SelectList<TrainStationListItem>("list");
_newTrigger = new Trigger<TextOutputField>("newtrigger", "new",
new TextOutputField("newtext",
new Text(new StringResource("NewTriggerKey"))));
_filterByTrigger = new Trigger<TextInputField>("filterbytrigger", "filterby",
new TextInputField("filterbytext",
new Text(new StringResource("FilterByKey"))));
}
public Trigger<TextInputField> FilterByTrigger {
get { return _filterByTrigger; }
}
public SelectList<TrainStationListItem> List {
get { return _list; }
}
public TrainStationDetailModel Detail {
get { return _detail; }
}
public Trigger<TextOutputField> NewTrigger {
get { return _newTrigger; }
}
}
public class TrainStationListItem : SelectListItem {
readonly Trigger<TextOutputField> _deleteTrigger;
readonly TextOutputField _name;
readonly TextOutputField _abbreviation;
public TrainStationListItem(string id) : base(id) {
_name = new TextOutputField("name",
Text.Empty);
_abbreviation = new TextOutputField("abbreviation",
Text.Empty);
_deleteTrigger = new Trigger<TextOutputField>(
"deletetrigger",
"delete",
id,
new TextOutputField("deletetext",
new Text("DeleteTriggerKey")));
}
public TextOutputField Abbreviation {
get { return _abbreviation; }
}
public TextOutputField Name {
get { return _name; }
}
public Trigger<TextOutputField> DeleteTrigger {
get { return _deleteTrigger; }
}
}
public class TrainStationDetailModel : ModelItem {
readonly TextOutputField _header;
readonly TrainStationDetailNamingModel _naming;
readonly TrainStationDetailGeolocationModel _geolocation;
readonly Trigger<TextOutputField> _saveTrigger;
readonly Trigger<TextOutputField> _cancelTrigger;
public TrainStationDetailModel(string id) : base(id) {
_header = new TextOutputField("header",
new Text(new StringResource("ChangeTheSettingsKey")));
_naming = new TrainStationDetailNamingModel("naming");
_geolocation = new TrainStationDetailGeolocationModel("geolocation");
_saveTrigger = new Trigger<TextOutputField>(id, "add",
new TextOutputField("savetrigger",
new Text(new StringResource("SaveTriggerKey"))));
_cancelTrigger = new Trigger<TextOutputField>(id, "add",
new TextOutputField("canceltrigger",
new Text(new StringResource("CancelTriggerKey"))));
}
public TextOutputField Header {
get { return _header; }
}
public TrainStationDetailGeolocationModel Geolocation {
get { return _geolocation; }
}
public TrainStationDetailNamingModel Naming {
get { return _naming; }
}
public Trigger<TextOutputField> SaveTrigger {
get { return _saveTrigger; }
}
public Trigger<TextOutputField> CancelTrigger {
get { return _cancelTrigger; }
}
}
public class TrainStationDetailNamingModel : ModelItem {
readonly TextOutputField _header;
readonly TextInputField _name;
readonly TextInputField _abbreviation;
public TrainStationDetailNamingModel(string id) : base(id) {
_header = new TextOutputField("header",
new Text(new StringResource("NamingKey")));
_name = new TextInputField("name",
new Text(new StringResource("NameKey")));
_abbreviation = new TextInputField("abbreviation",
new Text(new StringResource("AbbreviationKey")));
}
public TextOutputField Header {
get { return _header; }
}
public TextInputField Name {
get { return _name; }
}
public TextInputField Abbreviation {
get { return _abbreviation; }
}
}
public class TrainStationDetailGeolocationModel : ModelItem {
readonly TextOutputField _header;
readonly DoubleInputField _longitude;
readonly DoubleInputField _latitude;
public TrainStationDetailGeolocationModel(string id) : base(id) {
_header = new TextOutputField("header",
new Text(new StringResource("GeolocationKey")));
_longitude = new DoubleInputField("longitude",
new Text(new StringResource("LongitudeKey")));
_latitude = new DoubleInputField("latitude",
new Text(new StringResource("LatitudeKey")));
}
public TextOutputField Header {
get { return _header; }
}
public DoubleInputField Longitude {
get { return _longitude; }
}
public DoubleInputField Latitude {
get { return _latitude; }
}
}
Notice the lack of primitives and attributes, the presence of output fields, and routing/navigation using triggers. Input fields cater for values, change tracking, field level validation, provisioning of meta-data to be used as UX enhancements. The explicitness of the models allows for strongly-typed partial views. Text fields and formatted values work together with the current UI culture and resources to allow for seamless I18N. Making viewmodels visitable (e.g. solves the puzzle of how to capture form input in a model) and queryable (e.g. lookup of items in the model – hence the identifiers) will allow for even greater power inside controllers. Triggers and associated events work nicely together with a hierarchic statechart to control flow. The use of standardized model types allows for augmenting productivity in views by introducing helpers.
As always, I left out a lot of the finer details. If there’s one thing that should stick, it’s to take a deeper, harder look at how you’ve been dealing with viewmodels …
(*): not discussed here.