Tuesday, March 31, 2009

A Domain-Driven, DB4O Silverlight RIA

Build a Domain Driven Rich Internet Application using Silverlight, RIA Services and DB4O
I was recently ranting about Silverlight-2 and my annoyance with the WCF layer needed to serialize object instance data to the server. Well Microsoft must have had their mind-reading machines turned up high because the newly announced Mix09 Silverlight 3 & RIA services preview has solved nearly all the problems I was having.

Its the RIA (Rich Internet Application) Services that has the real wow-factor. This is Silverlight finally growing up. Instead of creating Frankenstein client-server apps crudely stitched together with WCF, RIA services allows you to treat your client and server as one, almost seamless application. You share domain design intentions between the client and the server so that your domain acts as you intended regardless of which side of the machine boundary you are on. This is how RIA development was always meant to be. 

You can download all the software kit you need from here the main Silverlight-3 site. I found the best way to get started was to watch the following Mix09 presentations then read the RIA Services Overview.
  • Building Amazing Business Centric Applications with Microsoft Silverlight 3
    "Come hear how simple it is to build end-to-end data-intensive Silverlight applications with the new set of features in Silverlight 3 and .NET RIA Services. Explore Silverlight improvements that help to enable rapid development for business applications and to make your development process more productive"

  • Building Data-Driven Applications in ASP.NET and Silverlight
    "Learn how Microsoft is simplifying the traditional n-tier application pattern by bringing together ASP.NET and Silverlight. Learn about patterns for working with data, implementing reusable and independently testable application logic, and application services that readily scale with growing requirements"
Of course Microsoft just could not help plugging their dystopian Data-Driven-Design vision. Look at the title of that second presentation for goodness sake. 

To counter this and to provide a solid Domain-Driven template for future Silverlight RIA apps I have created an example that does away with the monolithic SqlServer and Igor, the Entity Framework, in favour of an light, fast object database repository that promotes good Code Cohesion, Separation of Concerns and Inversion of Control.

Here is the tech I use in the example:

An Overview of the Domain Driven RIA Example
This example does just enough to highlight the primary functions of RIA services as I see them.
  1. You can define and use your domain objects on the server and then effectively re-use domain logic on the client. 

  2. You can transmit domain objects between the client and server without polluting your domain or requiring an additional DTO transformation layer.
The example is a stripped down version of an app I am currently writing so I have left in all the domain structure even if it is not actually used in the example. This makes the application ready to go if you want to start fleshing it out with your own code.

The functionality is quite simple. You are presented with a Silverlight 3 navigation application. There is a sign-in link that takes you a view containing a username and password field, sign-in and register buttons. 

The code works as you would expect with the additional extra that typing into the Name field triggers a 1 second timer that will check (on the server) that the name is unique. Clicking the Register button creates a new user in the server-side DB40 database. Clicking the Sign In button checks the db for the supplied credentials and, if they exist will return the appropriate User instance and cleverly navigate you back to the home view. 

If you have watched the videos you might at this point think I have cheated and used the built in Authentication Domain Service that come with RIA. Not so. That service relies on having a fat SQLServer file squatting in your solution and that is no good for a Domain-Driven purist. Instead I have implemented a very simple custom identity authentication that could easily be linked up to either Forms or Windows authentication in the usual way.



NB: If you get an error about the URI Prefix then you need to reset the startup application to DomainDrivenRIA.Web and the startup page to DomainDrivenRIA.SilverlightTestPage.aspx

18 comments:

  1. Thanks great work

    It looks like you really like RIA services. Hope that you will write more about it as you learn. Nice to see that RIA Services works without the entity framework :-)

    ReplyDelete
  2. Anonymous7:27 pm

    What makes this Domain Driven?

    ReplyDelete
  3. "What makes this Domain Driven?"

    Fundamentally because the design is not derived from a relational database. The objects are not just representations of db tables but model business entities directly. You will note this example uses db40, an object database. Since it is impossible (almost) to 'design' a db40 database then the domain design must come before the data-store structure. Thus the code must be 'domain-driven', as opposed to 'data-driven'.

    ReplyDelete
  4. Loved it. I'm tearing the example apart figuring out what's going on there.

    Crazy awesome. 10 levels above awesome.

    -- Justin Angel
    Microsoft Silverlight Toolkit Program Manager

    ReplyDelete
  5. mikekidder4:43 am

    Can't seem to get this demo to work for me. Getting internal errors (set breakpoint in VS to trace) when it attempts to lookup isUniqueName on the Sign-In page.

    Here is error I can pull from Immediate window:

    'DbServer.OpenClient()' threw an exception of type 'Db4objects.Db4o.Ext.Db4oException'
    base {System.Exception}: {"'db4o ServerSocket FILE: C:\\dev\\lab\\DomainDrivenRIA\\DomainDrivenRIA.Web\\App_Data\\DomainDrivenRIA.db4o PORT:0' is closed. close() was called or open() failed."}

    As you can see, has path info to db... any ideas?

    ReplyDelete
  6. In the Global.asax the following line is masking the true bug. Remove the call to the ServerClose() method.

    protected void Application_Error(object sender, EventArgs e)
    {
    DataService.ServerClose();
    }

    I think the root problem is incorrect security settings on the folder containing the db40 file. Check the folder allows read/write access for your web app account.

    ReplyDelete
  7. mikekidder6:17 pm

    @biofactal - thanks for the help! Commenting out the "DataService.ServerClose()" in Application_Error allowed me to run demo just fine. nice work.

    fyi - the error's have nothing to do with your code. There are two (hits Application_Error twice) exception error's that are bubbling out of SL3 beta ("Web.System.Profile.ProfileBase" and "System.Web.HttpApplication.Session"). I think this is the first sample I came across that is utilizing "global.asax". In fact, added it to another project and duplicated exception errors. So until these are handled, closing a database isn't a good thing... :)

    ReplyDelete
  8. Cool. Can you explain the differences between the AggregateRoot level and the Entity level? In my mind these could be consolidated but I am probably missing something.

    ReplyDelete
  9. I know DB4O has relatively weak LINQ support at the moment. While it can execute all LINQ queries, many are not executed natively, and instead fallback on LINQ-to-objects in-memory queries, which can be a lot of objects to activate in memory.

    How did you cope with this? Are all the LINQ queries in this sample executed natively in DB4O?

    ReplyDelete
  10. @Judah - I have never had a problem (yet) with DB40 performance so I have not taken the time to look under the hood at its LINQ support. FYI NeoDatis is very fast (and free) object db you might want to consider if performance is your prime concern. See http://sourceforge.net/projects/neodatis-odb/

    ReplyDelete
  11. Anonymous9:47 pm

    Can you update the code to work with the RTM version of silverlight 3? I tried running it and ran into a bunch of problems because there are breaking changes between the RTM and the beta you built this against.

    ReplyDelete
  12. Anonymous9:49 pm

    @Judah- or you can try just released version of siaqodb: http://siaqodb.com

    ReplyDelete
  13. Anonymous11:52 am

    Hi,
    the following code works fine and bind records with grid.
    EmployeeDomainContext employeeDomainContext = new EmployeeDomainContext();
    employeeDomainContext .Load(employeeDomainContext .GetEmployeeQuery();
    dgrEmployee.ItemsSource = employeeDomainContext.Employees;
    But if I want to get records from employeeDomainContext.Employees then I am not be able to get these records, even it shows its count to 0.
    Can I get these records in a variable?
    Thanks and Best Regards,
    Hafiz Muhammad Suleman

    ReplyDelete
  14. hello the domain driven is the latest domain in the web i study it and it really works so far cause i use it and is ok .

    ReplyDelete
  15. Anonymous4:11 pm

    nice

    ReplyDelete
  16. The Sample is a good start for bigger projects.
    I moved it to current Silverlight 4 tech.
    Sources here:
    http://cid-9803e97b07345d0c.office.live.com/self.aspx/.Public/DomainDrivenRIA-GettingStarted.zip

    ReplyDelete
  17. Thanks for the great port to Silverlight 4 jan. I have hilighted the link at the top of the post.

    ReplyDelete