Have you heard of SFD?

OK, you’ve heard of TDD and BDD, but have you heard of SFD? SFD stands for Simple-First Development. It’s a principle that we’re driving on as we move forward in our "Prism" (Composite WPF) project. Essentially it means that when we attempt to solve any problem, let’s start with focusing on the easiest path that addresses the most common cases rather than over-complexifying the simple cases in order to handle the edge cases.

Let me give you an example to show you how we are applying this principle. One of the features that CAB provided was allowing you to have modules that dynamically populate content anywhere on the screen by grabbing a handle to a Workspace that lies somewhere in the cloud by using it’s key name. The mechanism is so flexible that it allows Module A to load a new Workspace into an existing workspace in the Shell, and then Module B can come along and dynamically add content to that Workspace from Module A.

So what’s the problem with this? Well the problem is it introduces complexity, reduces debug ability, learn ability and in some cases may even post a security issue. It introduces complexity because suddenly I have things magically appearing throughout my app, that I may have to spend quite a lot of time tracking down. Depending on the complexity of this system, this could be an arduous task. Learn ability is affected because now in addition to understanding the tangible pieces of the system that i see, I have to be concerned about a whole other magical world that i don’t see. The security issue maybe that I may not want certain modules to be able to populate their content into a specific portion of the screen, but because everything is dynamic, I have no way of really controlling it. Even if I could do it, it would be very ugly.

As we’ve been working on "Prism" we’ve encountered this same problem, and although our first instinct was to use a similar pattern, we took another look with the Simple-First approach in mind. In "Prism" we have a notion of Regions which are similar to workspaces. A Region is a place to store content (views). At the shell level we decided that we should allow modules to push views into named regions in the shell. This makes sense because in a composite application you may have many teams that are all contributing content to the shell. You don’t want the shell to become a hotspot with every team having to modify it to hard-code where the injection of screens.

For example, imagine an ERP system where there are many different sub-systems that are accessed from a navigation tree on the left side of the screen. Each sub-system is potentially maintained by a different team.As you click on the sub-system, it loads up the screens for that  right portion of the screen.

Now let’s say that sub-system say Purchasing is actually deployed among several modules. So one module (PurchasingModule) loads up that has the main set of screens which are added to the shell. Then additional modules may load for the sub-system such as PurchasingHistoryModule. PurchasingHistoryModule then has some views like HistoryView that it wants display in PurchasingModule. But there are other modules as well that might contribute content to PurchasingModule like the PurchasingTrackingModule. But, PurchasingModule doesn’t have any direct references to those other modules.

Below is a diagram to illustrate this.

image

So how do we apply Simple-First to solve this problem without doing the module push. Instead we do an explicit pull model. But wait, PurchasingModule doesn’t have any direct references to the other modules right?. Aaah yes, so instead create an interface assembly that has interfaces for all the different views that PurchasingModule shares. Then each "child" module that loads can register it’s views when it loads with the IOC. The PurchasingModule’s Presenter however can contain explicit code that pulls the views from the container that it needs, and it can populate them as it sees fit.

For example see the code below (just for illustration)

class PurchasingPresenter {
   ...
   public void OnLoad() {
      IHistoryView HistoryView = container.Get<IHistoryView>();
      Put(HistoryView).In("HistoryRegion");
   }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Will this approach handle all cases? probably not. But we think it will address a large set, and for those cases will make the code more maintainable and easier to debug. Applying simple-first, we’ll stick with it for now. Once we see a need we’ll consider applying complex-next. ;)

This entry was posted in Composite WPF, Featured, prism. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://www.codebetter.com/blogs/glenn.block/ gblock

    Udi

    We are using Supervising controller. We’re calling it a Presenter, but it’s not Passive view.

  • http://udidahan.weblogs.us Udi Dahan: The Software Simplist

    Glenn and Jeremy,

    The difference between our points of view is that I’m using Supervising Controllers, whereas you’re using Presenters.

    Cloning by default at the repository isn’t helpful – I know, I’ve tried it. You actually get more clones that way as various parts of the system look at various objects. It becomes quite hellish to know which child belongs to which parent – are you cloning all children when you clone a parent? If so, you can’t just do: Parent.Children.Contains(someChild);

    There is only one case where you’d want to clone at the repository – and that’s when you’re cloning EVERYTHING – so that, for instance, you can draw all those entities on a map at a consistent snapshot in time.

    I suppose that if my Supervising Controllers were to talk to your Presenters, and those would talk to the views, that would keep us all happy. Thoughts?

  • http://www.codebetter.com/blogs/glenn.block/ gblock

    @Derek

    I re-read your comments. What i meant was this is one approach that will satisfy many needs. We’re not precluding from their being others, or you rolling your own that meets specific needs.

  • http://www.codebetter.com/blogs/glenn.block/ gblock

    @Ayende – The idea is to be more explicit rather than generic. Having a specific interface for each view means that the code is obvious rather than a generic IPurchasing view whose instances are simply iterated over and added.

    @Jeremy – Your right constructor injection is better in this case. In other situations though you might have conditional logic that determines what shows. This could be based on the context, user profile, customer profile, etc. In these cases I think having an imperative approach makes sense rather than having IOC magic. I guess one could use an IOC profile (similar to what you described you have in Structure map) or Contextual containers for these cases. Though I think having it inline would actually be clearer.

    @Udi – I tend to agree with Jeremy’s idea of pushing the clone to the repository rather than using AOP interception techniques on every view. This way with the respository the clone is handled once at creation time rather than at the moment the methods on the view / presenters are called.

    We’d be very interested in your suggestions around thread syncrhonization, etc. WPF does include a Dispatcher that handles marshaling to the correct threads using the Synchronization context. We were planning to have our pub/sub mechanism utilize it. Maybe we can set up a conf call with the team at some point to tap your brain.

    @Derek – The idea was to not prevent any sort of injection, but to have the parent doing the controlling rather than the child controlling the parent. It is possible we will come with guidance on both approaches. We’re taking a YAGNI approach (part of Simple-First ;) ) because we know there are many customers that won’t need that level of control. Not everyone who will approach using “Prism” is developing as complex systems as yours ;) For them having the push from the children seems like it could be unwarranted complexity.

    Don’t get me wrong, I am completely sold on the flexibility having the push from the children provides.
    Originally I was driving that the push capability was a MUST have, and i created several user stories to support it. But the more we talked about it, the more we realized that I was deriving this based on what we did in CAB. So in the spirit of challenging all assumptions, we rethought our plans.

  • http://ctrl-shift-b.blogspot.com Derek Greer

    Glenn,

    My comment about this technique leading to a violation of SoC wasn’t necessarily directed at your example. In the example you set forth, the Purchasing module has specific knowledge about what types of information and capabilities are going to be displayed on the screen (i.e. history and tracking). While it could be argued that purchase history and tracking aren’t inherent concerns of the main purchasing module, this level of separation may be adequate presuming the requirements necessitate the Purchasing view maintain knowledge about what is displayed and that it has limited needs for dynamic extensibility.

    The concern I have is that people will hear the recommendation that modules should always control what, when, and where things are displayed on views owned by the module and always use this approach irrespective of whether the host module really has an inherent interest in the content being displayed (abstracted by interfaces or otherwise). When considering SoC boundaries, I begin by isolating what is inherent to the problem domain at hand at an abstract level. Let’s call this “Boundary-First Development” for grins. It is often the case that a product has requirements to display what might otherwise be separate concerns on the same view. Taking a “View-First Development” approach for these requirements can sometimes lead to a lack of good separation of concerns once one starts compromising at the presentation layer. Of course, in some cases a compromise is the best approach over a pure SoC mandate which I think would probably be the case with your example.

    Concerning the simplicity of your example, my assumption was that you had a PurchasingView containing a placeholder designated for an IPurchasingHistoryView which would be populated by a PurchasingViewPresenter. Using the same MVP pattern used within SCSF, the PurchasingViewPresenter would have access to the PurchasingView through a View property. Once the PurchasingViewPresenter obtains a reference to an instance of an IPurchasingHistoryView from the container, it would be able to direct the PurchasingView to show this IPurchasingHistoryView in the appropriate location without the use of regions.

    If the view containing the placeholder for the PurchasingHistoryView were itself a child control of the main PurchasingView, or even a control within a view added from another module, this could still be done without the use of regions by locating the population of the IPurchasingHistoryView in a presenter for the containing control. I presume it too will be built up by the container and have access to the registered view. I set this forth only to say that if we are really looking for the simplest design within the recommendation that modules should not push content to other modules then it would seem this could be done without regions entirely, and with less magic involved. Additionally, if the strategy you are promoting is to not enable external modules to populate regions registered by the views of another module, this would seem to preculde having the Purchasing module populate the region of a view previously added from another module. Just because the Purchasing module may contain the parent view doesn’t change the fact that module X is having some other module push content to its view.

    That said, it sounds like the “pull only” strategy is only prescriptive in nature and will not be a limitation by the new region/workspace mechanism. I first read your closing comments to mean that the new way would not handle all cases, but if by this you only meant “… if restricted to this recommended usage” then I think all is well technically. I would like to recommend the inclusion of both styles in the documentation however, as I feel both are a legitimate need.

    Derek

  • http://codebetter.com/blogs/jeremy.miller Jeremy D. Miller

    @Udi,

    Nothing that Glenn is showing here is stopping you from doing your AOP trick with the Views. As long as the View/Presenter creation is always done with some sort of container you can use AOP. I still disagree with you about cloning the Model at the View by the way. The Presenter very frequently talks to the Model directly. That’s going to be blown if the Presenter and View have a different copy of the Model.

    Why not take care of the cloning at the Repository layer to eliminate the need for AOP altogether?

    @Ayende,

    What does the ResolveAll() really do for you though? It’s great for getting the pieces there at one time, but the reality is that you’ll need to interact with them and their presenters throughout the lifecycle of the screen. This isn’t like the web where you render a view and then it’s done. You’ll also deal with them separately. And when this screen is deactivated, all those other pieces need to go away as well.

    How are you going to message between the pieces? Yeah, you could go with scoped messaging with an event aggregator, but then you have more complexity of the type that makes CAB clumsy. It would be easier if the conversation between the pieces is more or less on a party line between only the top level Presenter and the children Presenters.

    The only way I see that being useful is if Prism goes down the CompositePresenter path where the top level presenter sees all of its children as nodes/leafs and can pass messages down the chain.

  • http://codebetter.com/blogs/jeremy.miller Jeremy D. Miller

    Why this:

    class PurchasingPresenter {

    public void OnLoad() {
    IHistoryView HistoryView = container.Get();
    Put(HistoryView).In(“HistoryRegion”);
    }
    }

    and not this:

    public PurchasingPresenter(IHistoryView historyView){
    _historyView = historyView;
    }

    or:

    class PurchasingPresenter {

    public void OnLoad() {
    Put().In(“HistoryRegion”);
    }
    }

  • http://www.ayende.com/Blog/ Ayende Rahien

    Glenn,
    Interesting perspective. I would probably opt for something like this:

    IoC.ResolveAll< IPurchasingView >().Each( view => shell.Display(view, view.Region) );

  • http://udidahan.weblogs.us Udi Dahan

    There are 3 big issues to be aware of – deadlocks, data races, and maintaining a high level of interactivity even in the face of lots of data coming into the client asynchronously.

    I have been developing these sorts of systems for over 5 years now, and I have found only one solution that covers all 3 all the time, every time – as in, a developer won’t be undoing this by writing one line of code the wrong way, yet that line of code being indistinguishable from those around it.

    You have to be aware of timing issues as well – you could get a message from the server before you’ve finished bringing up all the views and such – that might cause you to have 2 synchronization domains. Try debugging that.

  • http://www.codebetter.com/blogs/glenn.block Glenn Block

    @Udi

    Read your article. Interesting idea on using AOP to solve threading issues by automatically doing the cloning of entities. It sounds plausable, though makes me a bit nervous. Are you thinking of a dynamic proxy for interception?

  • http://www.codebetter.com/blogs/glenn.block Glenn Block

    @Udi

    We will be building for multi-threading and will certainly utilize SynchronizationContext to ensure everything is thread safe. In CAB we did this, for example the EventBroker could handle the thread marshalling and have the event handled on either the UI or the background thread. As to the offline scenario, we weren’t planning on focusing on it initially.

    What part about this is overly simplistic?

    @Derek

    How is it a violation of SOC, as the content that is being populated is sitting within the portion of the screen that is owned by the Purchasing module. Are you saying that the act of the Purchasing Presenter populating the HistoryView into itself is a violation of SOC???

    As to why not just have it in the view, the reason is because the “region” might exist as in one of the child views off of the main view, or a child of that child. The idea is that the parent is ignorant of where exactly that region lives, it just knows that it’s in one of it’s children. It might be sitting in a view that the Presenter previously added from a different module.

    The concept we are going for here is to say that the parent is in charge, and the children don’t just push content any where they want it. Although the other mechanism is more flexible, it’s more ambiguous wheras this is more explicit and easier to follow. It sounds like your concern is that the Parent has any knowledge of the child modules. By abstracting this through an interface though it doesn’t have direct knowledge. It can only go against the IOC and say give me IFooView.

    As I mentioned though, it’s not a one-size-fits-all approach. There’s nothing we are doing to preclude having a dynamic mechanism for injecting from the children, and you may very well need to. But, here we are saying that if you don’t need that, then this approach may be easier for you to work with.

    Anyway, none of this is set and stone. My goal here is to share the thinking.

  • http://udidahan.weblogs.us Udi Dahan

    I’m concerned by this possibly overly simplistic thinking.

    Are you taking into account the necessary multi-threaded issues for handling asynchronous communications, offline scenarios, and for smart utilization of multiple cores? If not, will developers adopting this framework have to rip and replace (again) in another 3,4,5 years?

    Have you taken a look at my posts on the topic:

    http://udidahan.weblogs.us/2007/12/26/what-makes-smart-clients-safe/

    http://udidahan.weblogs.us/2007/12/07/eureka-aop-is-the-final-piece-of-the-multi-threaded-smart-client-puzzle/

    What do you think?

  • http://ctrl-shift-b.blogspot.com Derek Greer

    Wouldn’t the simplest approach in this case be to have the presenter call View.ShowHistory(container.Get())?. It isn’t as sexy as the fluent interface call in your example, but I’m not really seeing any benefit in the incorporation of regions/workspaces here. If the Purchasing module is going to control the view, is there really a benefit to using regions?
    Additionally, the technique of creating a domain specific composer for a particular view is fine in some cases, but in others this technique would lead to a violation of separation of concerns or limit the extensibility of the solution out of the box.
    As a real world example, consider a parcel shipping company which has created a composite application to be given to high volume customers. The main modules of functionality within the company are shipping, shipment history, tracking, rate quotes, and address book management. To address the disparate needs of customers, the application is designed to allow the optional installation of each module in addition to the ability to install new modules for custom needs. The main shipment screen gathers information about the parcel, destination, and desired shipment method, but is allowed to be extended by other modules for including additional information or functionality on the main shipping view. In some cases the additional elements on the screen vary depending on both the information being entered by the user and external conditions. In this case, the shipping module has been completely separated from the notion of shipment history because there is no inherent need to display or persist history to perform the core shipping functionality. Therefore, the process of persisting the data when a shipment event occurs and the displaying of history related information or functionality is located within the shipment history module. Moving the display logic into the shipping module is first a violation of SoC and second would limit its extensibility.

    As another real world example, consider a computer manufacturer which has designed a composite application to be used within their call centers for sales and customer care. One of the modules within the application hosts a custom search framework for allowing disparate groups within the company to host domain specific search criteria within a centralized view. The main search view dynamically displays available search criteria based upon the registered fields from other modules and delegates search requests to registered actions based upon matching criteria. While in some cases the main view of the application is replaced with the results view of the module who received the delegated search request, in other cases a module may display intermediate results or error messages in a search results workspace on the main search view.

    An alternate approach to this design might be to modify the search framework to handle asynchronous results which may or may not contain a view to be displayed, and to impose upon modules the need to return a results view upon completion of the delegated action. However, this approach also doesn’t seem to require a separate abstraction technique for displaying the results. The framework could simply call View.ShowResults(moduleResultsView); in its asynchronous results handler.

    Overall, I see the concept of workspaces as an extensibility mechanism. If the module which exposes the region is also going to be the one who populates it, this becomes an unneeded level of abstraction.

    - Derek

    P.S.
    I also posted this comment on your other blog, but I see the discussion is happening here. Sorry for the duplication.

  • http://www.codebetter.com/blogs/glenn.block/ gblock

    @Stewart – I agree it is a specific flavor of the KISS principle.

    @edovale – I don’t see how you can connect one to the other. TDD drives out a design based on requirements. It does not dictate what the end result will look like, only that it is sufficiently decoupled to be be tested in a unit-test. You could build (and we did build) the same mechanism I described that we had in CAB using a Test-Driven apporach.

  • http://edovale.blogspot.com edovale

    This is nothing new. You get this for free when doing TDD.

  • Stewart

    SFD?? KISS!!

  • http://www.bluespire.com/blogs Christopher Bennage

    I definitely agree with your philosophy here. SFD sounds like a merging of XP’s “Do the simplest thing that works” and YAGNI.
    http://www.extremeprogramming.org/rules/simple.html

  • http://damieng.com Damien Guard

    This is the approach I’ve been using for a while now. It works really well providing you can discipline yourself to only do what works and not write any code at all that won’t be called and no code that will be exercises.

    You also have to make sure that when new functionality requires a design it is done cleanly and effectively.

    I covered this a little on my blog at http://damieng.com/blog/2008/01/31/pragmatic-dotnet-developer

    [)amien

  • Ejik

    Tell more about this apporoach. Its is interesting enough!