Feature by feature
I commented before that a feature is usually composed of several classes, running in different places and at different times, all working together to achieve a single goal. This post is meant to expand on this notion a bit.
Let us go back to the type of architecture that I favor right now. Note that this is a physical diagram only, without actually going into the details about how each of those is implemented.
Storage, for example, may be a relational database, a key value store, a distributed hash table or something else, depending on the requirements and constraints that I have.
More than anything, this diagram represent physical distribution and setup behavior. For example, you really want to backup all the storage servers, but an application server can just have a image made and that would be it.
The main reason for having a separation between web & app servers is mostly so we can place the web server in the DMZ and the app server in a more trusted location. But I digress.
I mentioned before that I don’t really like layering anymore, and that I tend to think hard before I create assemblies.
Here is a sample of some of the ideas that I have been working on lately. The idea is to formalize a lot of the notions that I talked about in my concepts and features post.
You can think about this as a logical extension to the way people build composite applications. We have the infrastructure, the idea of the base services that are being provided by the application, and then we have features.
Each feature is a complete story, which means that a feature will contain, in the same place, all the parts required to make it work. For example, as you can see in the project image, what we have is the Search feature, which contains some UI, the logic to control the UI on the client side and the server side, helper classes to manage that. In the Finders folder we have additional functionality that is specific for this particular feature.
The infrastructure knows that it needs to pull of of that together, so we base everything on conventions. For example, we have a routing convention for aspx pages, and for finding services or controllers.
From layering perspective, there aren’t any. Inside a feature, you can do pretty much whatever you want. There is usually some conventions around to help build things properly, but that is mostly about just getting things working, not to support layering. And I don’t have a problem with breaking the rules inside a feature.
Features that affect each other are rare. It usually only happen whenever we have to do things like “when you search for an order and you go to a particular order, we want to always go back to the search page, no matter how deep we went”.
Cross feature communication is done using pub/sub mechanism, most of the time.
Most of the ideas that I outline here are composite applications notions, just applied in a more general fashion. The end result is that you gain all the usual benefits from composite applications. You don’t step on another feature toes when you add stuff, and you have a stable base from which to work from. Each feature can be developed independently and is only worried about its own problems. The infrastructure is built during the first few features, but afterward it is mainly a stable platform on top of which we build.
I should note that notion is recursive. That is, we have a feature that keep getting expanded. At that point, we will probably treat it like the entire application, and build the infrastructure in place so we can drop additional features to the root feature. A common example of that would be to support additional authentication mechanisms for the Authentication feature.
What you call a 'feature' really looks like a bounded context..
Hi Ayende, this makes a lot of sense. It would work really nicely with BDD, with each feature having its behaviour all in one place rather than spread across several layers.
I completely agree. I took that same direction on a project some years ago (without really knowing DDD or all the cool new stuff).
Self-contained feature in Self-containg modules.
There is really no layering anymore ... I view it as a set of independent but cooperating 'cylinders' (going all the way top-down from the UI to the storage).
Just add a height dimension to your bounded context to make it become 3D and you get the idea ...
Also check this : www.goeleven.com/blog/EntryDetail.aspx?entry=133
Any plans on writing about the infrastucture that allows this type of architecture? Myself and I am sure others would love to hear your take on composite apps.
LIke Josh Rivers said in the comments of the previous post, I think we get the big picture but the level of abstraction of your posts is maybe too high. Or I'm too ignorant. But I think you will spread your message more easily with a little more of code/sample.
Please drop it in the queue
This is very interesting, and will give me much to think about in the future.
But what do to when two (or more) features have some common parts ?
For example, two features have many same elements (methods, etc) in SearchCriteria (according to solution example screenshot).
What to do ? Leave it and have duplicated code ?
Move common parts to infrastructure ?
Or create 'common features' place (directory in solution or separate project, .... ) and reference it in features ?
I like this idea ... but something is bugging when i see NewOrder folder in your project structure. Would you meant to have ExistingOrders, DeletedOrders, ShippedOrders, CancelledOrders etc., Is that viable to go by this approach? may be if Ordering System is what you intend to develop, then "NewOrder" is a feature or core functionality?. I also think features are implemented above on a basic functionality of the business requirements. I would develop a "OrderByPhone" feature for a "Grocery Ordering System" ... Or, may be we are confused with the term "Feature" ....we need to elaborate more ...
It's true that more and more people (me included) consider 'code reuse' as a bad thing (at domain level).
Think more 'service use' (this is achieved through pub/sub in your case).
I think, what you intend say is something simliar to Procedure Oriented Programming paradigm. Each function carries its own purpose, may not be generic in nature. I see the same inheritance happening in your approach.
I like the concept but I find it hard to see how you could separate things so cleanly.
Here's an issue that I'm dealing with:
The website I'm working on involved a database full of bands who submit tracks (among other things) and manage them.
Next, we have a media player that allows users to play theses tracks and also include them in personal playlists.
Those are 2 distinct features to me, but share lots of stuff.
I think this is a great topic and hope you have lots more to say about it! I find project setup, organization, configuration to be the hardest thing to manage as a developer. The code comes easy, where to put the code is what takes up a lot of my time.
I'm digging this, and would love to see more concrete examples like this. For example, how is the pub/sub implemented? (I'm sure it uses Rhino, but how exactly do the features consume it, etc) Just go ahead and release the code already =)
On saying bye to layering, I agree. I've been keeping most of our solutions to three assemblies: [Company].[Project].Web (or Win, etc), [Company].[Project].Library and [Company].[Project].Tests. Only when I absolutely need to use part and only part of nnn.library elsewhere do I split it up. Publishing interfaces for 3rd party webservice consumers to use without also publishing implementation, for example. Anything else is just academic.
That is where you can apply the notions of bounded contexts.
It is also important to understand that things like data storage are different between features.
How I store the tracks data for search may very well be different than the way I store them for playlists.
The concept of a NewOrder is usually a pretty big one in most system.s
Usually we have things like:
Orders - list / view / cancel
Sometimes it CancelOrder is complex enough to deserve its own feature. In that case, it will probably Orders\NewOrder, etc.
It is situation dependent, so I can't really answer.
But something like Searching on Documents or on Orders would likely have some minimal common infrastructure and exist in two places
I thought you were going to say that! The bounded context is still something I don't grok in code and I think that's something I need to resolve soon. When discussing concepts at a high level I find it easy to see the bounded context but just don't know how to code it.
Is it the case that DRY competes with bounded context? Or is that being over DRY (arid?) is an anti-pattern and bounded contexts is a technique to fix it...
I guess the first step is to acknowledge that I have a problem :-)
Reading things like...
...makes me sad :o/
Hah. Not to speak poorly of the academics in the room, Patrick. ;) Maybe I overstated. I mean only that I've found I can still separate code without necessarily having an assembly for each little thing.
I'm probably a bit off-topic but I was curious if having multiple Domain classes mapped to the same table is how you implement a bounded context?
I think I've struggled with ORM because I still think data-centric and pretty much map one Domain class to a table.
This is a possibility, but the more likely scenario is that you actually have different tables for each of your contexts. How you store the data and what data you store is unique to each context. So yeah - keep it simple, map one domain model class to a table, accept the fact that some data will be duplicated across bounded contexts, and lean on pub/sub to keep everything in sync.
It reminds me CAB the first time I saw it. But how do we address the required distribution for windows / smart / RIA / Client Ajax applications? We would still need to fit in some sort of a distribution and features would be splitted atleast for the UI (& its surrounding patterns).
Intrestingly, all these looks like lean SOA, just binded to a single platform :).
That sounds like a completely different world than I'm used to. I take it that the database is highly denormalized then? Duplication of data in the database always seems like quite a headache.
So pub/sub would mean that if a band renamed a Track, pub/sub would dispatch an event that this happened, and any publishers (eg: Playlists and their tracks) would subscribe to this event a rename their tracks accordingly?
As always, the answer is "it depends". In a large distributed system different bounded contexts often have their own data storage, and their own domain models. Pub/sub is used to handle business events that are being published by the different bounded contexts such that subscribers are able to receive those messages and act on them in a manner appropriate to their context / domain model. The notion of an order might exist in many contexts, but everyone has their own interpretation of what it means to them. If you are building a big system this introduces complexity up front but makes it much easier down the road. If it's a small system you might not want to go this route.
For your Track renaming scenario you could be storing all the data in the same database / tables, just using pub/sub to enable async behavior in your system - "TrackRenamed" is published, and the Playlist context/service/etc... subscribes to that and starts renaming tracks contained in playlists (assuming they're stored in a denormalized fashion to make reads fast, otherwise if it's just a foreign key relationship the track is already renamed :) Later on down the road when you have another part of the system interested in that event it's simply a matter of adding another subscriber for that message type.
Thanks again Matt!
I believe I need to read up on bounded contexts because that seems to be the main thing that's not clear to me (the implementation not the concept). Thanks for providing a direction for me to research further.
Very interesting! I've been using something very similar, but have not found a good way to develop my aspx files & images in the feature project. Could you elaborate a bit on how you do this?
Where are the aspx files for your feature at runtime? Do you have a post build event / nant task that copies all feature pages to a specific location beneath your web app? Are they embedded?
From the looks of your project icon, you are using a class library or custom project type. How are you getting around Visual Studio hiding the aspx item template for your project?
In this instance, it is just playing with routing, building them dynamically based on conventions