Context: Building a smart client application on the .NET platform where you have a complex database model with a high number of columns involved. The natural application style is a typical data driven CRUD. There’s also a fair bit of server side logic in some cases, and somewhat complex validations. You have full control of client and server, so the need for interoperability is at a minimum.
This question has a lot of details, apologies for that, but it is because I want to set the proper context for the answers.
A few more assumptions
- As is not uncommon in the Microsoft world, most previous applications have been written with DataSets, so it is the best known technology for the developers involved. But let’s say the developers are well versed with OO thinking as well.
- You will need to run validations on both client and server.
- You don’t show most data in tabular form.
- This is not an intranet application, so you can’t assume too much about the bandwidth
The biggest question: Datasets or objects?
If you go for datasets you have a few positives and negatives
- In terms of positives: You get a bit of Microsoft support in terms of getting the data out of the database, getting the data over the network and returning changed data over the network in smaller chunks – since you can specify only to send changes. Sending less data is good since there’s potentially quite a bit of data involved.
- The negatives are: In terms of validation, business logic and so on, you get a procedural form of code and you don’t get the benefits of object oriented code –behavior and data together, a more natural style of working and thinking about what you’re doing, and possibly closer ties to the validation logic. You can also look away from the benefit of putting the dataset in a grid, since that is not the common use case.
If you go for objects, it’s the same drill, but there are more options involved:
Positives: Behavior and data together. Validation logic closer. Easier to see and understand the relationships between objects. More readable code. Easier to unit test.
But there’s quite a few choices and work you need to do as well:
OR/Mapping
- Getting the data from the relational model to objects. OR-mappers aren’t that complex, and will be able to handle it well. But it adds on to the development time.
Contract mapping
- It’s generally good practice to map data from server-side objects to contract objects, likely DTO’s. Since this is an application with a good fit for CRUD style architecture, DTO’s don’t really add much value to the picture, just mapping work.
Shared code
- You can go for a shared code scenario, where the assembly with the domain data and logic is available on both client and server side. That’s tight coupling, but it’s not necessarily bad when you have a naturally tightly coupled client-server app.
Whether you choose adding a contract layer or not, you have large object structures that must be sent over the wire
Since we’re controlling both client and server, the transport and encoding should be binary encoding over TCP. That will help.
With datasets, you have the option of only sending the changes back. Sending the entire object structure back and forth is a likely performance issue.
An option to sending the entire object structure, is to somehow identify the changes involved (Create, Update, Delete), and sending only information about that. In theory it’s not too hard to send the aggregate root ID to the server as well as the changes, ask the server to lazy load the aggregate root, perform the changes made, and then save again. But the big complexity involved is identifying the changes done. Do you ever go for this approach? Why? How exactly do you do it?
Presentation
The exact UI technology is not really that important for the question, WinForms, Silverlight or WPF is possible. Let’s take the assumption that we are using WPF since it’s a new smart client. That means we have two way binding and can use MVVM properly.
The objects bound to in the user interface will need to implement INotifyPropertyChanged and raise an event each time a property is updated. How do you solve this? If you go for the shared code scenario, you can add it to the domain objects, but it that will involve adding code and logic on the server side that is never meant to be used there. The separation is more natural if you go for contract objects, but that’s not a lot of value added just to add a layer of mapping.
Technologies
There are a few technologies available that can help with solving some of the issues, but that often complicate others. Do you use them, or build things from the ground up yourself?
**
- CSLA is possible, but it makes unit testing harder, and seems to add a tighter coupling to data access. It does help with a number of the problems, but personally I have no competence with this technology, so whether it is a great fit is a bit hard to say.
- WCF RIA Services would be possible for a Silverlight solution, but there are definitely limitations involved. Size of data is one.
- WCF Data Services is another approach to getting something quickly up, but REST isn’t much help, and you also lack the validation support you have in RIA Services.
Summary
If you’ve gotten this far, I hope you have an idea of where I’m going with this. I’ve tried to scope it down to avoid talking about everything at once, but distributed development is complex, so you have to consider many parts.
Update
Thanks for the responses guys! I was trying to ask the question open enough to open for varying answers, but specific enough to deal with a few not uncommon requirements.
There are different considerations that have different pros and cons, and which varies from system to system. Each usually adds to the complexity of finding a solution. One of the points of this questions was to get answers particularily with a few extra requirements that don't neccessarily fit directly to the one answer that is often the correct one today - with task based UI. I'm not a "CRUD-guy", if you will. But a few systems, for various reasons (most often legacy), have a good fit for CRUD.
Many business apps have similar demands that pull in different directions:
Business related
- View: Showing data to the user and updating the same data (Reads and CUDs - Create, Update, Delete)
- Validation: Business rules
UI related
- Validation: UI rules
- UI updates: Code specific to just getting the UI to update on object changes (INotifyPropertyChanged)
Network related
- Data size: Amount of data you send over the wire
DB related
- Lazy loading
SRP/reuse related
- Mapping: Caused by multiple layers of objects / separating concerns
Maintenance/change related
- Changes: Adding new information (columns/fields)
- Amount of code
- Reuse and "reasons to change"
Technical limitions
- Change tracking
But these are just some very specific ones. You always need to know which "-ilities" you find most important, and thus what degree of scalability, availability, extensibility, interoperability, usability, maintainability and testability you need.
If I would try to generalize something for most situations, I'd say something like:
Client
- Use MVVM for separation and testability
- Create the VM on top of DTOs
- Implement INotifyPropertyChanged in the VM.
- Using XamlPowerToys, Postsharp or some other means of helping with this can be worthwhile
- Separate Reads and CUDs in the UI
- Make CUDs task based, and use commands or similar to send those operations to the server side
Server
- Tailor-make a dto per screen
- OR use the multi-query approach described by Ayende in http://msdn.microsoft.com/en-us/magazine/ff796225.aspx
- Use automapping to avoid the tedious, manual and completely unrelated to the problem you are trying to solve step, that mapping is
- Let the domain model be concerned with business operations primarily, including the CUD-related operations, and not reads
- Avoid reusability that adds to the number of reasons to change
- Avoid encapsulation issues
- (And by that enable CQRS style architecture and possibly separate scaling of reads and CUDs in time)
- Try to find a validation approach that fits well with what should be done (Good read: http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/02/15/validation-in-a-ddd-world.aspx)
Is this neccessarily the approach I would take in this particular situation?
Well, that's what I wanted to start a discussion on :) But seems that was harder than I hoped (besides the two of you).