'Prism' RI first drop
Today is a historic occasion for the "Prism" team as our first drop of our Reference Implementation (RI) hits codeplex. In this post I will describe to you what the RI is, and what you can expect to see when you download it.
Disclaimer: This will evolve (and change) significantly. At this stage of the game you should be looking at this to get a sense of where we are heading and to see the different patterns we are applying. What is in this drop is not fully baked (or even half-baked :) ). This is not a CTP, this is not a BETA, it's our team being as visible as possible into our development efforts.
<WARNING>Long post follows</WARNING>
Why a Reference Implementation?
Before we jump into the details, let's take a step back and talk about what an RI is, and why we are creating one. An Reference Implementation is essentially a sample application that is designed to illustrate a standard (design patterns, technical specifications) in order to help others implement their own versions of the standard. The RI is not a real production application however it needs to illustrate real-world scenarios, in order to be relevant. Within patterns & practices these RIs are super-important in that they are one of the major stores from which we mine our guidance.
In order to achieve the "real-worldness" of our application, we work with our customers and partners. They tell us "Yes, this is something we do (or could see doing)" or "No, you are out of your mind" :) We're certainly not experts in every business domain, so we rely on the ecosystem heavily.
As we build out the RI, we see different recurring patterns emerge around the technical challenges that the RI is illustrating. As as we see these patterns we continually refactor in order to address them in a cleaner fashion. As part of this refactoring we apply key software principles such as Separation of Concerns (SOC), Single Responsibility Principle (SRP). We also look to implement known design patterns as part of the implementation.
What about the framework?
Over time as we refactor more and more, a framework / set of libraries begins to also emerge. We're not setting out with any hard and fast expectations about the framework, though we know it is likely one will exist. If we've done our work right, and stuck to the YAGNI principle, then this framework will not be the answer to world hunger, but rather it will be an answer for the RI and for applications with similar patterns. That does not mean that it won't be applicable for a wide range of applications, as if the scenarios in the application are common, then that may very well be the case.
It's up to you
I can't reiterate this point enough. We're not guaranteeing that the patterns / framework used in the RI will be right for your scenarios. Only you can make that decision. What we can say is that we are confident that the techniques / patterns we're using are a good fit for the scenarios in the RI.
Not the only fit!
This is just one of the many different ways to skin a cat. It is a way, not the only way. For your scenarios it may even be the wrong way.
Prism RI
In the case of "Prism" we're building a WPF application that is a Composite client. That means in addition to looking at the essential aspects of a Composite, we want to look at what is special about building them in WPF. At a high level we want to illustrate the following concepts:
- Shell - The application should contain a place to host different UI components. The shell itself should be lightweight and not "know" about what contents it will contain. As it is essentially dumb, the shell needs to expose a way for itself to be populated by others.
- Layout - The shell should contain place holders for content that will be displayed in the shell.
- Modules - The application should not be monolithic rather it should be developed as a set of separately tested and deployed modules. These modules can be developed and maintained by separate teams.
- Initialization
- Modules need to have a common place for initialization code.
- Loading - Modules can be statically or dynamically loaded.
- Static modules are modules that are directly referenced and the shell is hard coded to load them. The shell kicks off the load, but does not have any knowledge into what it is loading.
- Dynamic modules are modules that are discovered either through sweeping a folder (Eclipse style) or through configuration.
- On Demand. In future releases we will be looking at how to specify that a module loads on demand.
- Styles - Modules should be able to be individually styled by a designer using Expression blend. Global styles should also be able to be specified
- Initialization
- Views - Modules should contain different content which is proffered up to the shell.
- Human Designers should own the UI (either that or a developer playing the role of the designer). This means not making hard and fast rules about how things look, including menus and toolbars.
- WPF Databinding should be levered wherever possible.
- WPF Commands should be used for binding UI actions.
- The UI should illustrate making good use of data visualization
- The UI should illustrate floating / overlaid content as well as dynamic content that appears and disappears
- The UI should be testable and implement appropriate design patterns for SOC. The likely candidates we've been looking at our Model View Presenter and Presentation Model.
- Communication - Different components within the application should communicate with one another whether they reside in the same or different modules. This needs to happen without the modules requiring hard dependencies on one another. Communication should be thread-safe.
- Services - The application should expose a set of services that can be accessed by the modules in a loosely coupled manner.
As we develop the app we have a few over-arching goals or architectural qualities that we are thinking about (this is a partial list of special qualities in addition to the usual suspects). Some of these are motherhood and apple pie, so we are developing detailed acceptance criteria (though not an exhaustive list) of specific things we want to test to see if we are meeting the mark.
- Simplicity - Simple, but not too simple. For the simple cases, getting there should be simpler than for the complex cases. This is basically applying the 80/20 rule otherwise known as the Pareto Principle. It means that 80% of the cases can be handled by addressing 20% of the causes. The remaining 20% of the cases can be addressed with other means. Simple also implies that we are limiting the number of abstractions as too many certainly leads to more complexity.
- Usability - In addition to making the concepts easier to swallow, we're thinking about how you apply those concepts hence the features. This means we are making a tradeoff between having more features in order to have less features that are more usable.
- Subsetability (no I didn't make it up) - Ability to pick and choose capabilities turning them on or off or as I mentioned not requiring you to take the whole kitchen sink. At high level we think this means if all you want is the composite UI capabilities but have not bought into dynamic module loading (or vice versa) then you can choose. At a more detailed level it means if all you want to do is pull in our EventBroker (we will probably have one) then you can.
- Composition - This is the whole Object Composition vs Inheritance topic. Without getting in to all the details, it means that you will have a lot more flexibility in how your application wires itself up into the framework i.e. via interfaces vs requiring you to inherit from a whole set of core constructs. You've heard of the old Hollywood principle of "Don't call us, we'll call you". Well this is more of a "I'll call you and give you directions on how you can call me".
- Letting you use the DI / container framework of choice. This means using Unity, Castle Windsor, StructureMap, Spring.NET, or even a Hashtable :)
- Allowing you to leverage existing utilities. This means using Enterprise Library's logger, Log4Net or your own logger if you so desire.
- Compatibility - Not requiring you to start from scratch. Essentially this means being able to take the concepts and apply them to an existing application. The focus here is around WPF not WinForms (though we will look at WinForms via Crossbow). As an example we have acceptance criteria that defines taking an existing WPF application and adding UI composition to it without having to completely rework it.
The Story
For the "Prism" RI, we decided to do a stock trader application. There are several reasons for why we chose this. For one thing, trader apps make good use of data visualization which is a core aspect of why one would use WPF. Also, several customers we spoke to are building trader or trader-like apps. Third, we presented the idea to our advisory board and they liked it :). The stock trader app we are envisioning has several core components. You can see a screenshot below which illustrates what you will see in the drop.
Core Components
Positions Screen
Contains a list of funds that you have invested in along with relevant information about each position. As you select a position, the Trend Chart changes to reflect the current position. Items within the position may have associated news which can be access by pressing the "News" button.
Watch List
A list of funds that you are watching. Notice that the list currently appears when you hover on the "Watch List" tab. There's also a pushpin for making the list more permanent. "From the toolbar, you will notice an "Add to Watchlist" button which will in the future allow you to enter in a ticker symbol and add it.
Portfolio Breakdown
Displays a pie chart with a breakdown of your portfolio.
News
Displays news articles. News is populated by various different sources and related to various actions. Currently it is only populated based on clicking the "News" button. In the future it will also be populated based on the trend chart.
Trend Chart
Displays trend data for the selected symbol. In the future you will be able to hover over points in the trend line and see a popup of related news links for that fund on that date. You will then be able to click one of the links and populate the news tab with that article.
Buy / Sell
The Buy / Sell screen will allow you to create a batch of buy / sell transactions. This functionality does not yet exist, but it will in the future. We think this screen is critical for exploring more complex scenarios.
Implementation Details
I'll be following up with much more detail in future posts, as will the rest of my team. For now let's just paint a high-level picture of each of the players.
Solution Structure
We ship two solutions, one for the RI + Unit tests, and the second for the Acceptance tests. I'm not going to cover the acceptance tests for now, but if you are interested go check them out. You'll need Thoughtworks' White to be installed in order to run the tests.
When you open the solution, you may be terrified when you see this....
Yes, we have 15 projects already! These are not "frameworky" type modules, rather they are the different modules maintained by the teams, as well as the unit tests. To get a simpler view, go and collapse all the folders until you see this.
StockTraderRI is the main project that you want to execute. As to the rest of the folders
- Modules - Contains the various application modules which are maintained by the various teams
- Common - Contains reusable Framework / Library code.
- Unit Tests - Contains VSTS unit tests. We have about 60 so far, and the number grows. We are building out the app in a Test-First design style, so we're writing all of our tests first and stubbing out / mocking implementation.
As to the projects themselves, here is a break down (not of the test projects) along with interesting tidbits.
- Prism.csproj- Contains the core services and utility classes that are reusable. Currently this only contains the MetaDataInfo class, and the RegionManager service and it's related classes. We expect this will grow.
- Prism.Interfaces.csproj - Contains interfaces for common services and types.
- StockTraderRI.csproj - Executable which launches the application. Currently it contains only an App.xaml, Shell.xaml and a Bootstrapper.
- The Shell is simply a WPF Window, and does not require you to inherit from any special base class.
- You'll notice within the shell we have Regions. Regions are placeholders for content. You'll see we're using an attached property (prism:RegionManager.Region) in order to automatically register these regions. Once a region is registered, it can be accessed by the RegionManagerService.
- NewsRegion is the region where the news is displayed. It is a special kind of region where each item added appears in a separate tab.
- The Bootstrapper does a few things:
- Initializes the container (can be replaced to use any container but currently uses Unity)
- Registers some core services
- You'll see a RegionManagerService. This service is what is accessed by various modules to populate the shell. When a module has content it needs in the shell, it grabs the region manager.
- Shows the shell
- Intializes the modules.
- You'll notice here that we are doing static module loading, meaning the shell actually does have hard-references to the modules, though they don't reference it. The shell does not know anything about the modules other than in interface it can call to initialize them., We're doing this deliberately because in some smaller apps, it may not be a requirement for dynamic loading.
- Each module implements an Initialize() method. In this method is where services / views / presenters etc are registered with the container, it is also where views will be shown and added to the Shell.
- The Shell is simply a WPF Window, and does not require you to inherit from any special base class.
- StockTraderRI.Infrastructure - Contains shared domain and presentation models that are used across modules.
- StockTraderRI.PositionModule - Displays the different positions including the portfolio breakdown.
- Contains an AccountPositionService which retrieves the list of positions to be displayed.
- Contains a PositionSummaryPresentationModel which is an observable collection of positions.
- Adds the TrendLine and PositionGrid. Although the TrendLineView actually exists in a separate module, that module does not inject it. Instead, the PositionModule asks the container to give it an instance of ITrendLineViewPresenter, and has the view show it. Because the PositionsModule gets that pointer, it can use that interface to notify as a new position gets selected. In CAB this would have traditionally been handled through an EventBroker, as the Trend line would have been injected. Here we are being more explicit, with the benefit of being able to directly call between the modules without needing EventBroker. We are not ruling out that we will have an EventBroker type functionality as we believe it is needed, though not in this instance.
- RI.WatchListModule - Manages the watch list functionality.
- Contains a WatchListView and WatchListView presenter which is shown during the initialization. You'll notice that the Presenter drives all the interaction, so that in the WatchListModuleInitializer we are creating a WatchListPresenter through the container. The Presenter then accepts a WatchListView which is passed to it's constructor. The DI container will find handle injecting this.
- Also contains the AddWatchControl. This does not have a Presenter as it does everything through databinding.
- StockTraderRI.NewsModule - Manages the aggregation and display of news. We imagine a news team would handle locating news from various services that relates to the funds being displayed.
- Contains a NewsFeedService which is responsible for retrieving news for a particular ticker symbol. Returns NewsArticle instances that encapsualate the news. Currently it only supports one article per symbol.
- Contains a NewsService which is responsible for displaying the news for a particular symbol. The NewsService works with the RegionManager to see if the article already exists. If not, it creates an instance of the INewViewPresenter. The presenter itself actually populates itself to the region by grabbing hold of the RegionManager itself.
- One important aspect of this is that when the News article is shown there is additional information that needs to be provided to the tab, such as an icon. Instead of having the NewsService have to manage this information, we've encapsulated it within the view itself. The ShowNews method on the presenter sets Metadata (MetadataInfo) on itself. The tab region then pulls this Metadata to set it's icon and such.
- StockTraderRI.MarketModule - Manages retrieving market data for each symbol and displaying the trend chart.
- Contains MarketFeed and MarketHistory services, the MarketFeed service contains the current information, and the MarketHistory contains historical information with fund fluctuations.
- TrendLineView uses a hybrid of Model View Presenter and Presentation model for presenting the data. The Presenter sets up databinding. The Presentation model contains all the data displayed in the trend line and is bound directly to the UI. The TrendLineView / Presenter are registered with the container on initialization but not displayed.
OK, I'll stop now as I can see myself going on for hours and hours. As I mentioned above this is just the beginning, and there's much more to come in the future. This includes documentation around the RI itself and the patterns in play.
Go get the bits here!
We WELCOME your feedback ;-) As my team-mate Michael always says, "Have Fun!"
Comments
Anonymous
March 12, 2008
PingBack from http://mdavey.wordpress.com/2008/03/12/prism-early-release-available/Anonymous
March 12, 2008
Well, I haven't looked at the code yet because codeplex seems to be having some maintenance fixes, but I'm going to go ahead and give you my gut feeling. You've got a fairly simple application that already has 15 projects. You may be missing your first two goals already: Simplicity and Usability. I'll have more feedback after I drill through the code.Anonymous
March 12, 2008
Congrats on starting to get something out and initiating the feedback cycle. Under what licensing will this be released? will we be able to run the source in Mono (maybe with some tweaks)? And what subset of this do you think would apply if one wanted to cover similar scenarios in Silverlight?Anonymous
March 12, 2008
@Rob yes there are 15 projects, but more than half are for unit-tests. The rest are mostly the actual modules for the application which have been put in separate modules in order to illustrate the composition. @edjez Prism is licensed under MS-PL, so I don't think there are any issues preventing it's use on Mono. I will check though. As to SL, we just don't know. My guess is a lot of the "framework" functionality will port to SL. The one issue will be around DI and whether or not any will work on SL. We have an adapter for the container, so you can supply your own. Even if it means writing one in Silverlight :)Anonymous
March 12, 2008
Glenn, People are porting Windsor to Silverlight, actually.Anonymous
March 12, 2008
Glenn Block & the Guys from Patterns & Practices just released to the public Prism First DropAnonymous
March 12, 2008
Looking forward to digging in and trying some of it out on an RI for my own company. @Ayende - Great news that Windsor will show up on Silverlight. Re:@Rob - He expresses a widely shared feeling that project prolixity is a menace and sure sign of undue complexity. When a simple RI seems to require 15 projects, the whole enterprise risks derision. I agree that it is a smell. But not all smells are rotten and here the profusion of projects could be perfume if we understood the intent. I don't agree that a "compositional" approach necessitates distinct projects. Not in the least. But an appropriate backstory could justify this solution structure. Perhaps five different teams developed the application infrastructure and the four modules independently. It is customary(although certainly not necessary) for teams to segregate their efforts by project. The backstory might have told of a requirement that the modules be independently deployable; user roles determine who gets which module. In which case the modules must be divided into separate projects (assemblies). That said, the burden remains for PnP to articulate the principles and practices guiding the structuring of VS Solutions and Projects. We should resist the temptation to factor into projects merely because we imagine a need for it down the road. Example #1. Jim Newkirk recently opined that it would be best if the tests were part of the project whose code they tested. Such a practice would eliminate five projects on the spot. Is there something wrong with that suggestion? Example #2: The startup project, StockTraderRI could be combined with StockTraderRI.Infrastructure until there was a compelling reason to break it out. The same might be said of the ChartControls which could have been in their own folder within Infrastructure. I can hear someone countering that there would be unnecessary dependencies (e.g., Infrastructure would gain a dependency upon PresentationCore and the ReachFramework). If so, explain why this is so terrible, given that the shell and ChartControls will be loaded under all circumstances anyway. There may be good architectural reasons ... in which case we owe our audience an explanation. Net: I think Rob is off-base for this RI but his provocation is worth attention. All of which makes me very happy to see Prism out and about. Great job of communication!Anonymous
March 12, 2008
The comment has been removedAnonymous
March 13, 2008
I have to agree with Ward, especially after the cursory look at the code. If you publish some sort of back story that details the hypothetical business reasons for the architecture, that would help people to understand better; and to better determine if this approach meets the needs of their situation. I don't think that the tests should be combined with the code they test, however. In any case, I'm going to have a lot more feedback, particularly on the code itself; some specifics, some generalities. I think its too much to post here. Do we have an established mechanism for this feedback yet? Should I just post it on the codeplex site?Anonymous
March 13, 2008
My piece of feedback is to disagree with the RI's lack of a dynamic module loader service, and to hope that this represents only the early state of the project and not a firm decision to eliminate such a thing. IMHO, one of the main distinguishing features of a composite app is the lack of such compiled-in dependencies. I would request that the P&P team state whether a dynamic loader service this is currently planned or not.Anonymous
March 13, 2008
@Rob, @Ward There are specific business reasons for each of the separate modules. I will do a follow up post to explain this. The infrastructure module cannot be conslidated as it contains types that are shared across modules. It will create a cirular reference if the modules reference it, as the shell references them. @David It is planned. Our initial focus is around UI Composition, and not dynamic module loading. We still have modules only they are statically referenced. Over time we will add dynamic loading functionality, but we want customers to have the option of using static loading if it is sufficent. The plan is to have 2 RIs, one that does UI Composition with static module loading and a second that demonstrates UI Composition with dynamic loading. When we get to working on dynamic loading we will look at the configuration story, and also explore the loading on demand story. Loading on demand may be something we demonstrate in a quickstart instead of the RI itself.Anonymous
March 13, 2008
@Rob, you can use the forums.Anonymous
March 13, 2008
@Guillem Thanks for the feedback, I have passed your suggestion on to the team.Anonymous
March 14, 2008
Do we have any design document or block diagram that helps me better understand how Prism works under the hood? I did not find any document in the .ZIP file downloaded from http://www.codeplex.com/prism/Release/ProjectReleases.aspx?ReleaseId=11615Anonymous
March 14, 2008
A few days ago we have published the first drop of our Reference Implementation on Codeplex . For moreAnonymous
March 14, 2008
A few days ago we have published the first drop of our Reference Implementation on Codeplex . For moreAnonymous
March 18, 2008
@saurabhd no not yet, we will have docs coming soon. We will start having background info materializing on codeplex.Anonymous
March 22, 2008
Congratulations! I think one of the interesting subjects to address is an MVP adoption to WPF and specifically to the benefits of data binding. Tried going over view-presenter couples in the drop but didn't find a clear pattern. What's the latest on that? AcerAnonymous
March 22, 2008
@Acer, Thanks. We are addressing this. Essentially you'll see three core patterns coming into play around testability of the UI. Model View Presenter, Presentation Model and Command. Command is pretty clear when to use due to the inherent platform support in WPF, however we are doing some exploration on how to best wire up WPF commands across modules. Because of the premier status binding has in WPF, Presentation Model plays very well. In most cases you will see we are doing a hybrid of the two, with the Presenter being responsible for populating the Presentation Model so it can be bound. The Presenter will also handle other UI gestures as appropriate. For example if you look in the Postions module at the PositionSummaryPresenter, you will see the PopulatePresentationModel method which will populate the positions. This model is already is then bound to the UI by the Presenter which sets the Model property on the view. The view then sets the DataContext to this Model. I'll most likely do some posts on this in the future, and we will have documentation around the RI that explains what we are doing and why.Anonymous
March 23, 2008
Glenn, It would be great to see your output on MVP and binding. The code has a major change vs SCSF where in SCSF the view refrences the presenter and here it does not. Guess you wanted to eliminate the dependency but with that you took the ability to bind UI to presenter properties - which we found to be extremly usefull. Also "manually" setting properties on the view which then fills an internal model which is bound to the UI seems to me somewhat non-WPF, probably can do better by leveraging binding more (again, bind to data which comes from outside the view) AmitAnonymous
March 27, 2008
Now that Web Client Software Factory Shipped and the team is focusing on Prism (the composite scenario