We are now turning towards a somewhat more involved example after a little break in part 8 – the orders. We’ll make a bit more complex filter for the list of orders while keeping it paged – the IOrdersViewModel is so akin to ICustomersViewModel that I’ll jump to the differences in the IOrderFilter:
public interface IOrderFilter : IBaseFilter
{
DateTime? OrderDateFrom { get; set; }
DateTime? OrderDateTo { get; set; }
ICustomer Customer { get; set; }
IAvailableCustomerSelection AvailableCustomers { get; }
}
public class OrderFilter : BaseFilter, IOrderFilter
{
private readonly IDefaultEntities _defaultEntities;
private ICustomer _customer;
private DateTime? _orderDateFrom;
private DateTime? _orderDateTo;
public OrderFilter(IAvailableCustomerSelection availableCustomers, IDefaultEntities defaultEntities)
{
AvailableCustomers = availableCustomers;
_defaultEntities = defaultEntities;
}
…
public ICustomer Customer
{
get { return _customer ?? _defaultEntities.DefaultCustomer; }
set
{
_customer = value;
OnPropertyChanged(“Customer”);
OnFilterUpdated();
}
}
}
public class DefaultEntities : IDefaultEntities
{
private static readonly ICustomer _defaultCustomer = new NullCustomer();
public ICustomer DefaultCustomer
{
get { return _defaultCustomer; }
}
private class NullCustomer : ICustomer
{
public long Id
{
get { return -1; }
}
…
public string CompanyName
{
get { return “No customer”; }
set { }
}
}
}
So, a few notes:
What is the deal with the default customer?
The DefaultCustomer is a dummy-implementation of ICustomer that is guarantied to exist only once in memory as it is statically new’ed up and the class is private to the DefaultEntities-class. Basically, it represents null everywhere a Customer is allowed to be null at some point. This means that in the IOrderFilter implementation, the Customer is allowed to be null – representing ‘not searching by customer’. So, we get the ability to show null in a combobox’ed list.
Why -1 in the Id property
To make absolutely certain that Default customer is never saved, I’ve arranged for it to behave ‘badly’ in the persistence layer. Id -1 is an obviously wrong value – meaning that it does not belong in a persistent state.
Why isn’t available customers a part of a ViewModel – and what are they doing in a filter?
Since I’ve chosen to implement the filter with a combobox containing all customers, I need to gain access to these – and I’ll need it again when we turn to Order Detail, so I decided to put it in it’s own class. I didn’t make it a part of the ICustomersViewModel since it isn’t really a list, I want to interact with – it’s readonly by nature – so it received the name IActiveCustomers. I don’t want to read the database for customers everytime, I new up an order – so, it is basically a form of caching.
Other than that, Orders behave much like Customers when we look at the list – so, next and last chapter will focus on the detail editing of Orders.
About the series:
The solutions introduced here are not entry-level material. You should understand generics and basic patterns to fully utilize the material presented. The solution represents about 3 years of experience working with one ERP-solution. Some of the choices taken are heavily influenced from the large-scale solution and may seem overkill for the simple examples here, but the idea is to show the end-result of my experience and hopefully you as the reader will at least see a new way of doing things. The solution is built in .Net 4.0 with heavy use of WPF’s capabilities. Here you can find the source-code for the series. You are free to use this code for any purpose, but it is presented as-is with no guarantees. Enjoy!
Okay, we have successfully created a small application in part 7 where the user can add, edit and remove customers. Not bad considering the small amount of code in the solution. Still we have a lot of classes involved and this alone creates a lot of complexity. We are paying the price for a very stable and easy-to-maintain solution down the road. I hope this will be easy to see when we start implementing entity number two – the order.
The first 7 posts in this series have been focused on taking baby-steps in order to get to the point where we have something to show. I’ve intentionally split this series into two – to show how I work when I develop ERP-solutions. I call it vertical slizing – ie. taking one core functionality and making that ‘finished’ before turning to the next functionality. Many of the ideas behind the design of the model is taken from Domain Driven Developement while a lot of patterns shine through in the class-design.
Room for improvement
Some things in the solution are intentionally kept simple to focus on the M-V-VM pattern. For one thing, the repositories are _very_ simple, but since most ERP solutions will be designed around an ORM, this is actually a non-issue. The repositories in the solution are fine for testing and not having to depend on schemas and other expensive (especially to change) friction makes you productive. If it costs you next to nothing to change into a better design, you will be easier to persuade to do it. The longer you can postpone the data-access, the clearer your approach will be when you decide to tackle it.
Next the CustomerCommands class is not exactly how I would do it in production code. I would put the wiring into a mediator class instead of a command – much of it is plumbing, and could easily be contained in a separate class. Some call it a Controller (MVC) – some call it a Presenter (MVP). I usually end up with a constellation of ViewModel, Model, View, Controller and perhaps a helper or two for specialized views (usually drag and drop functionality or a secondary ViewModel). Usually my Entity commands are one-liners using the IoC container and calling a method on the resulting class.
Third, we have the views – these are obviously not receiving the loving they are entitled to. WPF is a rich language and you can make absolutely stunning things here, but maybe I’ll look into that in another series. Again, the focus here is the ViewModels – not the Views.
Last, but not least – the order in which I showed the classes are not necessarily the order in which I design production code. The order has been slightly modified to better fit the logic flow of the series. I usually start out with the Model – when I have that working, I turn to UI basically specifying the bindings I’ll need from the ViewModel and then designing that last. So far things have been relatively straight-forward, but when we turn to Orders, things will become somewhat more complex.
After this short stop – I’ll turn to orders in part 9.
About the series:
The solutions introduced here are not entry-level material. You should understand generics and basic patterns to fully utilize the material presented. The solution represents about 3 years of experience working with one ERP-solution. Some of the choices taken are heavily influenced from the large-scale solution and may seem overkill for the simple examples here, but the idea is to show the end-result of my experience and hopefully you as the reader will at least see a new way of doing things. The solution is built in .Net 4.0 with heavy use of WPF’s capabilities. Here you can find the source-code for the series. You are free to use this code for any purpose, but it is presented as-is with no guarantees. Enjoy!
Okay – part 6 was ViewModels – now it is time for the eye-candy and unleashing the power of the ViewModels. All views (save one) will implement the IView<TViewModel> interface which has a single method: void SetViewModel(TViewModel viewmodel). I’ll explain later why there are no more methods…
Now for the Views – I’ve implemented two relatively simple views – CustomerDetailView and CustomersView which will handle editing a single customer and showing customers respectively. I’ll not show the code here as that is not the intention of this series, but only give a few small notes:
Why is there so many small sections – shouldn’t it all be in one long XAML?
It’s a matter of preference mostly. I like to declaratively show what a view is comprised of. Some use comments inside the xaml, but I prefer having small sections that can be tied together into a larger picture. When I come back to the view 6 months later – I can quickly see where to go to change things. Some might think that it is for reusability, but that is only true for a very few controls – namely the DeicdeSaveSection and the PagingSection which will be used in the OrderDetailView and OrdersView later. When it comes to templating this is a much more valid point.
The Shell
Every application needs somewhere the user can select what he wants to look at. I call it a shell – a term stolen from Prism (the Composite Application Guidance from Microsoft). Basically, the Shell keeps track of what is possible in any given context and handles where the Views should be placed and how. The Shell does not know how many Views there are, nor what they show – it is a ‘dumb’ container responsible for layout’ing the views in relation to each other. One other responsebility is showing the ‘switchboard’ – here the ShellCommands.
There is a bit of styling in the Shell to allow it to show web 2.0-style popups. This will effectively enforce the problems around multiple transient Customers/Orders – making sure that only one new entity can be edited at the same time. Again nothing fancy, but you may notice that the Shell is responsible for creating the Commands it can activate. This is a bit of a shortcut – but since noone else is using the ShellCommands, I chose the pragmatic simple way.
Now, the glue to making it all function – we need to somehow create all the classes involved in the views. And we want to make sure that none of our classes need to know actual implementations, but only rely on the interface. This is called Dependency Injection (DI) and Inversion of Control (IoC). I chose something that is a bit of an anti-pattern – the static IoC. Since this is still a simple example, I don’t want the added complexity of passing the IoC container around at startup. So, bear with me on that one. Okay – a brief explanation of the static IoC – basically I use the App.xaml.cs to register which implementations I will use for each interface. Then when it comes to actually use the implementations, I call the Resolve<TService> method on the static IoC. Then the IoC will ‘magically’ find all the dependencies and resolve everything so that I do not need to worry about it. (It is of course not magic – the IoC container keeps track of which interface should resolve into what and instantiates them according to the constructor parameters of the class to be resolved). The resolve-method is used in the CustomerCommands and ShellCommands classes.
For http://goblincave.net/06/2010/m-v-vm-building-an-erp-system-%E2%80%93-part-8-of-n-rounding-up-customers.php, I’ll take a breather and look back on what we have actually done so far.
About the series:
The solutions introduced here are not entry-level material. You should understand generics and basic patterns to fully utilize the material presented. The solution represents about 3 years of experience working with one ERP-solution. Some of the choices taken are heavily influenced from the large-scale solution and may seem overkill for the simple examples here, but the idea is to show the end-result of my experience and hopefully you as the reader will at least see a new way of doing things. The solution is built in .Net 4.0 with heavy use of WPF’s capabilities. Here you can find the source-code for the series. You are free to use this code for any purpose, but it is presented as-is with no guarantees. Enjoy!
Okay – so in post 5 we have managed to get and find data – now we need to be able to perform interactions with the data. We need an implementation of interface ICustomerDetailViewModel:
public class CustomerDetailViewModel : EntityViewModel<ICustomer, ICustomerRepository>, ICustomerDetailViewModel
{
private string _accountNumber;
private string _companyName;
public CustomerDetailViewModel(ICustomerRepository repository)
: base(repository) { }
protected override void ResetEntity()
{
_entity.AccountNumber = _accountNumber;
_entity.CompanyName = _companyName;
}
protected override void CopyFromEntity()
{
_accountNumber = _entity.AccountNumber;
_companyName = _entity.CompanyName;
OnPropertyChanged(“AccountNumber”,”CompanyName”);
}
public string AccountNumber
{
get { return _entity.AccountNumber; }
set
{
_entity.AccountNumber = value;
OnPropertyChanged(“AccountNumber”);
}
}
public string CompanyName
{
get { return _entity.CompanyName; }
set
{
_entity.CompanyName = value;
OnPropertyChanged(“CompanyName”);
}
}
}
public abstract class EntityViewModel<TEntity, TRepository> :NotifyPropertyChangedBase, IEntityViewModel<TEntity> where TEntity : class, IAggregateRoot where TRepository : IPersistRepository<TEntity>
{
protected TEntity _entity;
private readonly TRepository _repository;
private Action _onClose;
protected EntityViewModel(TRepository repository)
{
_repository = repository;
SaveChanges = new RelayCommand(DoSaveChanges, CanSave);
CancelChanges = new RelayCommand(DoCancelChanges);
}
private void DoCancelChanges(object obj)
{
ResetEntity();
Close();
}
protected abstract void ResetEntity();
public ICommand CancelChanges { get; private set; }
private string[] _validationErrors;
public string[] ValidationErrors
{
get { return _validationErrors; }
}
public ICommand SaveChanges { get; private set; }
public void SetActionOnClose(Action action)
{
_onClose = action;
}
public void SetEntity(TEntity entity)
{
_entity = entity;
CopyFromEntity();
}
private bool CanSave(object obj)
{
if(_entity == null) return false;
var isValid = _entity.IsValid(out _validationErrors);
OnPropertyChanged(“ValidationErrors”);
return isValid;
}
protected abstract void CopyFromEntity();
private void DoSaveChanges(object obj)
{
_repository.TrySave(_entity);
Close();
}
private void Close()
{
_entity = null;
if (_onClose != null) _onClose();
}
}
The CustomerDetailViewModel is pretty basic – it only handles user interactions and undo-functionality if the user cancels. There are a ton of ways to do undo (haha) – here I chose a very basic one which will not work for advanced entities. Here an alternative could be to reload it from the database or let the entity implement IEditableObject (often creating an exact clone to reflect the changes).
The base class is somewhat more interesting – a note on the implementation:
Why is there no IsValid method?
I’m relying on the SaveChanges command’s CanExecute – this will run everytime a property is changed in the view, and since this triggers the IsValid of the entity – these things happen automatically in the Command-structure of WPF.
You might think that it looks kinda simple for such an important task – but that is the whole point, I want my classes to be small and easy to comprehend. The inheritance adds complexity, but not a whole lot, and I do not need to explain a lot with comments or the like. The less lines of code, the less can go wrong – and the less will need to be maintained. Now all that is left for the solution to be able to handle customers is a few views and a few finishing touches.
This will be in parts 7 and 8.
About the series:
The solutions introduced here are not entry-level material. You should understand generics and basic patterns to fully utilize the material presented. The solution represents about 3 years of experience working with one ERP-solution. Some of the choices taken are heavily influenced from the large-scale solution and may seem overkill for the simple examples here, but the idea is to show the end-result of my experience and hopefully you as the reader will at least see a new way of doing things. The solution is built in .Net 4.0 with heavy use of WPF’s capabilities. Here you can find the source-code for the series. You are free to use this code for any purpose, but it is presented as-is with no guarantees. Enjoy!
We introduced commanding in part 4 and so far we have mostly been trying to get to the point where we can actually see something on the screen – and now we will look at the data behind the view. Enter the ViewModel. The responsebility of the ViewModel (in my version, at least) is to act as a mediator between the (dumb) View and the Model. For the ICustomersViewModel we have three things in play:
- DataAccess (we need some data to show)
- Filtering and paging (we need to be able to find the relevant data)
- Commands (we wish to interact with the data)
DataAccess is a whole chapter in itself. To keep things simple here – I use an in-memory database to serve this functionality – it is hidden behind the ICustomerRepository interface:
public interface ICustomerRepository:IPersistRepository<ICustomer>
{
IEnumerable<ICustomer> FindByFilter(ICustomerFilter customerFilter, int currentPage, int pageSize);
int GetCountByFilter(ICustomerFilter customerFilter);
}
public interface IPersistRepository<TEntity> where TEntity:IAggregateRoot
{
bool TrySave(TEntity entity);
bool TryDelete(TEntity entity);
event Action RepositoryUpdated;
}
Why the IPersistRepository interface?
Basically it will help me when I build a base-ViewModel when we go to edit entities – remember this is the refactored interface, so some responsebilities have been isolated into separate interfaces.
Why two functions for the data?
The GetCount function is there to initialize the Paging for the ICustomersViewModel – basically we need to know how many customers fulfill the ICustomerFilter to show an accurate number of pages. This function will only be called initially and when conditions change (the ICustomerFilter is updated or an entity changes or added). This is also the reason for the RepositoryUpdated event on the IPersistRepository interface.
Why a return type of bool to TrySave and TryDelete?
Well, you are allowed to yell YAGNI at this point – because currently I don’t use the return value for anything. From experience it will be needed later when users start crying for reasons why it didn’t save their changes. Right now, we don’t care why it failed (when it fails), so we don’t know if we will throw an exception or return an array of strings (which is the reason, why I didn’t at an out string[] argument to the TrySave and TryDelete).
Now, let’s look at the CustomersViewModel implementation – I won’t go into details about the base-class, as it is mostly plumbing code to make paging work – it is not really relevant for the points, I’m stressing. You are welcome to leave a comment if you need an explanation for it, but take a look at in the solution accompanying this series of posts:
public class CustomersViewModel : PagedViewModel, ICustomersViewModel
{
private readonly ICustomerRepository _customerRepository;
public CustomersViewModel(ICustomerFilter customerFilter, ICustomerCommands customerCommands, ICustomerRepository customerRepository)
{
CustomerFilter = customerFilter;
CustomerCommands = customerCommands;
_customerRepository = customerRepository;
}
public ICustomerCommands CustomerCommands { get; private set; }
public ICustomerFilter CustomerFilter { get; private set; }
protected override int GetNumberOfEntities()
{
return _customerRepository.GetCountByFilter(CustomerFilter);
}
protected override IEnumerable GetPagedData(int currentPage, int pageSize)
{
return _customerRepository.FindByFilter(CustomerFilter, currentPage, pageSize);
}
protected override void SetUpdateListeners()
{
_customerRepository.RepositoryUpdated += ResetPagedData;
CustomerFilter.FilterUpdated += ResetPagedData;
}
}
Okay – not much going on here – as I stated in the beginning – the CustomersViewModel is only a mediator. It doesn’t do a lot of logic – basically it just makes sure that calls from the UI is handed down to whatever Model is behind it (in this case basically just read-only data from database). All logic concerning editing and how it is done is handed off to the ICustomerCommands interface which I’ll touch when I have implemented how to actually edit a customer. A few points of interest:
What’s up with the SetUpdateListeners?
The base-class (PagedViewModel) needs to know when to reset paging. Basically we are telling it to reset whenever the Filter is updated or the repository is updated (a save or delete happened).
In part 6, I’ll look into the interactions with the customer.
About the series:
The solutions introduced here are not entry-level material. You should understand generics and basic patterns to fully utilize the material presented. The solution represents about 3 years of experience working with one ERP-solution. Some of the choices taken are heavily influenced from the large-scale solution and may seem overkill for the simple examples here, but the idea is to show the end-result of my experience and hopefully you as the reader will at least see a new way of doing things. The solution is built in .Net 4.0 with heavy use of WPF’s capabilities. Here you can find the source-code for the series. You are free to use this code for any purpose, but it is presented as-is with no guarantees. Enjoy!
After part 3, we are getting ready to tie things together and as many might know, WPF contains the really nice ICommand interface that implements well with buttons, menus and inputbindings (go .Net 4.0). Back when I started out with WPF I used a lot of standalone implementations of the ICommand interface. It made for very nicely encapsulated commands, but there were problems. I put them into ResourceDictionaries for sharing them between views, but soon I had an overwhelming number of them, and naming became a problem. Also, maintainability proved troublesome. Having scattered business-logic scattered out into all these little classes that I couldn’t use DI (Dependency Injection) on proved a maintenance nightmare and ResourceDictionaries proved brittle (magic strings, anyone?). Testability wasn’t all that great either, and most of the commands were mostly doing boxing and un-boxing and IoC (Inversion of Control) resolution.
So, while a definete improvement over the old eventhandlers of Windows Forms, I wasn’t totally convinced it was the perfect solution – and then I stumbled upon the M-V-VM pattern (thanks to Josh Smith). This proved an eye-opener for me – binding to commands was a really neat trick – and the most important? I had functionality for views together in one spot. ‘What can I do with Customers in this view…? Oh look 4 commands in the CustomersViewModel – guess I can do those 4…’ Testing is a lot easier and I get the added bonus of one-place boxing and un-boxing. Wait a minute… I use the same CanExecute across commands? Well, not exactly – I wouldn’t want to introduce some sort of inheritance hierachy for something as simple as ICommands – no, enter the RelayCommand (again – thanks Josh).
Now, it may seem that Josh Smith is a demi-god of M-V-VM – and he is, but I do not agree with him in everything he does. I have read his article on MSDN, and I disagree on some of the choices he takes, but we do really different things, so that is to be expected. But you get to be the judge at which approach fits you better.
public class RelayCommand:ICommand
{
private readonly Predicate<object> _canExecute;
private readonly Action<object> _execute;
public RelayCommand(Action<object> execute)
{
_execute = execute;
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public void Execute(object parameter)
{
_execute(parameter);
}
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
public event CanExecuteChanged
{
add
{
if(_canExecute != null)
CommandManager.RequerySuggested += value;
}
remove
{
if(_canExecute != null)
CommandManager.RequerySuggested -= value;
}
}
If I had to point at one single class that has made the largest impact on my coding style – this is it. It has a bit of the functional programming that I’m still a beginner at, but realizing the potential of every day. In the ERP-system I’m about to construct, I will have 2-3 implementations of the ICommand interface – one of them being the RelayCommand. It saves me a lot of boiler-plate code (at least 8 lines per command – and that adds up when you have hundreds of commands). And DRY! Many commands will in the CanExecute method simply figure out if a parameter is of the correct type – and for a Customer – it is usually whether it is a customer… so, the four commands can share the same Predicate. I know it may seem like small pickings, but it was one of the few places, refactorings couldn’t really touch in standalone commands.
So – in the implementations of the first ViewModels, you will see a lot of RelayCommand’ing.
In part 5, I’ll finally start looking at the actual implementations of the ViewModels.
About the series:
The solutions introduced here are not entry-level material. You should understand generics and basic patterns to fully utilize the material presented. The solution represents about 3 years of experience working with one ERP-solution. Some of the choices taken are heavily influenced from the large-scale solution and may seem overkill for the simple examples here, but the idea is to show the end-result of my experience and hopefully you as the reader will at least see a new way of doing things. The solution is built in .Net 4.0 with heavy use of WPF’s capabilities. Here you can find the source-code for the series. You are free to use this code for any purpose, but it is presented as-is with no guarantees. Enjoy!
After part 2 – we now have a model to work from – and it is time to create the first ViewModel.
After some carefull consideration – I’ve chosen that we need a list of Customers where we can perform CRUD (Create, Read, Update, Delete), to make things a bit interesting, we want to have a paged view as we expect our customers to range in the thousands – so we have the basis for our first ViewModels interface (and a few helpers):
public interface ICustomersViewModel:IPagedViewModel<ICustomer>
{
ICustomerFilter CustomerFilter {get;}
ICustomerCommands CustomerCommands {get;}
}
public interface IPagedViewModel<TEntity>:INotifyPropertyChanged
{
IEnumerable<TEntity> PagedData {get;}
ICommand MoveFirst {get;}
ICommand MoveLast {get;}
ICommand MoveNext {get;}
ICommand MovePrevious {get;}
int CurrentPage {get; set;}
int PageSize {get;set;}
int NumberOfPages {get;}
}
public interface ICustomerFilter:IBaseFilter
{
string AccountNumberSearch {get;set;}
string CompanyNameSearch{get;set;}
}
public interface IBaseFilter:INotifyPropertyChanged
{
event Action FilterUpdated;
}
public interface ICustomerCommands
{
ICommand NewCustomer {get;}
ICommand EditCustomer {get;}
ICommand DeleteCustomer {get;}
}
It might seem a bit complex for a simple list, but remember this is the refactored version after several other ViewModels have been built, so DRY has been applied. A few notes:
Why a separate class for the Commands – and why don’t you use the ApplicationCommands?
To keep the code straightforward and keep responsibilities apart (SRP), I try to keep the Commands seperate from the logic driving the querying. The reason, I don’t use ApplicationCommands is that in .Net 4.0 I get bindable InputBindings which will allow me to avoid code-behind routines to relay keyboard shortcuts – and ApplicationCommands will force me to do write new code-behind to wire them up to my commands. I would rather assign keyboard shortcuts than have messy code-behind.
What’s up with the Filter – why not publish just the string property in the viewModel?
A valid point – my experience is that Filters change as the customer gets to know the product – so I’d rather change a small filter class (plus the repository) without having to change the ViewModel class. Basically, I’m applying a change-rate dividing logic here – things that change often are isolated. Also, I want to make it clear what the search property is all about – it is easier to explain that all the properties in the filter-class are for reducing the query and everything in the ViewModel has something to do with interacting with the query-result (here changing pages and so on).
Why an extra event on the filter – it already has INotifyPropertyChanged?
I want to be explicit about when to update the data in the list. I might want it to update less times than properties change – or might delay it (for users with rapid keystrokes there is no sense in loading the list 6 times within one second). Also, if I end up with ranges – e.g. dates, I might update both dates and only want one update (e.g. when one date is entered that will result in an invalid date range – I will change the other date to produce a valid range).
Next, we need a detail view to edit the customers:
public interface ICustomerDetailViewModel:IEntityViewModel<ICustomer>
{
string AccountNumber{get;set;}
string CompanyName{get;set;}
}
public interface IEntityViewModel<TEntity>:INotifyPropertyChanged
{
ICommand CancelChanges { get; }
string[] ValidationErrors { get; }
ICommand SaveChanges { get; }
void SetActionOnClose(Action action);
void SetEntity(TEntity entity);
}
Not many surprises here – we need to be able to edit the customers details, as well as designate whether we want to commit changes.
What is the SetActionOnClose method about?
This is a hook to handle removing the view. Remember that the ViewModel has no knowledge of the View on top of it – and has no idea where it is placed, so we need a way to tell the ViewModel what to do when it is time to close.
In Part 4, I’ll look at implementing commands.
About the series:
The solutions introduced here are not entry-level material. You should understand generics and basic patterns to fully utilize the material presented. The solution represents about 3 years of experience working with one ERP-solution. Some of the choices taken are heavily influenced from the large-scale solution and may seem overkill for the simple examples here, but the idea is to show the end-result of my experience and hopefully you as the reader will at least see a new way of doing things. The solution is built in .Net 4.0 with heavy use of WPF’s capabilities. Here you can find the source-code for the series. You are free to use this code for any purpose, but it is presented as-is with no guarantees. Enjoy!
As discussed in part 1, we need to implement the model somehow. The solution-source code shows the refactored result. There are 3 major implementation details that I will touch here:
Validation
I chose a solution here that is a bit more basic than I like in a production system, but still very capable – and easy to expand into a separate class when needs expand. I quickly added a generic base-class to handle all the ‘plumbing’-code (DRY applied harshly here). Below is the result for validation:
public abstract class AggregateRoot<TAggregate> : IAggregateRoot
{
protected static IListt<ValidationRulet<TAggregate>> _validationRules = new Listt<ValidationRulet<TAggregate>>();
…
public virtual bool IsValid(out string[] errors)
{
errors = _validationRules.Where(rule => rule.IsBroken(this as TAggregate)).Select(rule => rule.BrokenMessage).ToArray();
return !errors.Any();
}
…
}
Basically, I use a ValidationRule class to encapsulate validation – these rules can be very simple, or very complex and are applied sequentially. This means that I can get a list of all the things that are wrong (very usefull in UI for complex types). I hate (and I really do!) long if-statements thrown together with loads of unreadable conditions. For very complex rules, I would use a composite rule that would break all the scenarios into manageable chunks. To keep things simple, I didn’t add an IValidationRule<TAggregate> interface which I normally would to handle the composite rules. I favour encapsulated validation ie. the objects are capable of validating themselves. It’s mostly a matter of religion – but I like to have things encapsulated.
Equality and Identity
Equality and Identity may be the same thing – and then again might not be. We have two basic scenarios in our simple application – objects can either be an aggregate root (thus having an Id) or they can be a ‘simple type’ with little concept of identity. For aggregates, I’ve used the Id to determine whether we are talking about the same order. I cannot rely on two instances of any given order to be sharing the same memory allocation (ie. reference equality) – so, I’ll have a fall-back plan. I use the Id to do this. This leaves a problem with transient orders (ie. orders that have not been assigned an Id yet). I’ll not address this here (if I had used GUIDs this would not have been a problem), but just accept that if equality is important – users are only allowed to new one order up at the time…
For ‘simple types’ – here the order lines – I don’t really care if they are equal, but to facilitate later performance-runs, I will use the state to determine if these are equal – also, this enforces the idea that order lines are expandable and are replaced rather than copied – so, all state is used to determine if they are equal.
Collections
I didn’t discuss a major design decision in part 1 – why did I use an IEnumerable instead of an IList in the IOrder interface? Because I don’t want developers using all of the IList interface without my say-so. I might decide that all orderlines added will trigger some side-effect – or if the order is billed, you can no longer add order lines etc. Now, this can be easily added later by adjusting the AddOrderLine method. If I had OrderLines.Add(..) scattered all over my code-base, this would be very hard. Also the problem around .Remove() (see Part 1 for an explanation), could introduce nasty side-effects if developers are not consistent in how they treat the collection. Basically, I’m telling my colleagues – “This is what you can do – everything else is out of the question.” Internally I use an IList for convinience. I don’t expect an order’s orderlines to be so large that it will matter performance-wise – so, an IList is chosen here.
Part 3 will focus on the first ViewModels.
About the series:
The solutions introduced here are not entry-level material. You should understand generics and basic patterns to fully utilize the material presented. The solution represents about 3 years of experience working with one ERP-solution. Some of the choices taken are heavily influenced from the large-scale solution and may seem overkill for the simple examples here, but the idea is to show the end-result of my experience and hopefully you as the reader will at least see a new way of doing things. The solution is built in .Net 4.0 with heavy use of WPF’s capabilities. Here you can find the source-code for the series. You are free to use this code for any purpose, but it is presented as-is with no guarantees. Enjoy!
I’ve decided to post a series on how I would start out designing an ERP solution with focus on Model-View-ViewModel. To fully understand the reasoning, this first part mainly discusses the model in which I work. Many of the later choices should be clearer if you understand the reasoning behind it.
First of, there is no ‘one way’ to do M-V-VM – and how you use it, will determine, how (and if) you benefit from it. I design ERP-systems (Enterprise Resource Planning) which are typically rich on business-logic and have high demands for user-friendliness especially in dataentry screens. Thus, if I did UI-designing like I did back in the VBA-days, I would end up with a jumble of spaghetti-code with UI-concerns creeping into the cracks of my business-objects. So, the main focus for me is getting rid of all things not business in nature and isolating it in ViewModels. Also, the domain of ERP-systems is typically used by different clients – ie. webservices, smart-clients and EDI-interfaces and having to maintain each of these with the same business model is critical. Performance is another key-point as the system is so large that basically the entire organisation will use it – in differnet ways and in different areas – and I cannot allow one users actions to impact all others. Also, I use WPF as the front-end and WPF has a lot of functionality that lends well to M-V-VM.
To keep things simple, the domain I use in this series is much less complex, but will scale very well for very complex scenarios. We will have Customers, Orders and OrderLines as the main objects – the business model looks as follows:
public interface IAggregateRoot
{
long Id{get;}
bool IsValid(out string[] errors);
bool IsTransient{get;}
}
public interface ICustomer:IAggregateRoot
{
string AccountNumber{get;set;}
string CompanyName{get;set;}
}
public interface IOrder:IAggregateRoot
{
DateTime OrderDate{get;set;}
ICustomer Customer{get;set;}
IEnumerable OrderLines{get;}
void AddOrderLine(IOrderLine orderLine);
void ClearOrderLines();
}
public interface IOrderLine
{
decimal Quantity{get;}
string Description{get;}
decimal PriceEach{get;}
}
Okay, should be kinda simple, but there are a lot of design-choices already made. Below, I’ll outline some of the reasons behind them:
Why does IOrderLine not inherit from IAggregateRoot?
Order lines belong to an order – and must never be allowed to exist with-out an order. Also, there is no complex logic in an orderline – it could easily be a struct in essence. People at this point may be thinking “but… when it goes to the database, it should have an Id!” – I disagree. An Id would make sense if we are allowed to update the orderline outside the context of the order – I have effectively made sure that cannot happen (in the domain – the gloves are off if you have access to the database).
Building an ERP-system is all about keeping options limited to the ones that make sense business-wise. The simpler the domain – the simpler the maintainance.
Why are there no setters on IOrderLine – how will you ever correct an order line?
I won’t correct it. I’ll replace it. Basically, the orderline is treated as if it was a simple type eg. an integer. I could have an RemoveOrderLine method, in the IOrder interface, but it would not make sense here. If I were to do that, how would I know I was removing the correct one? There is no Id to tell me which one is which – and there is no logic telling me that I cannot have two identical order lines. I would have to make sure, I was referencing the correct orderline (in memory) before removing it – adding needless complexity. Also, when the user edits the order, I’ll update the orderlines in one fell swoop as I’ll show later.
Why does the ICustomer interface not have an IEnumerable of the Orders?
Because it does not need it (yet, anyways). If I want to find all orders for a specific customer, I could query orders instead of querying the customer. Again, simplicity in the model is key – and two-way connections is complexity (who will update the other – how do I make sure they are saved correctly… etc.). Avoiding deep object dependencies is also key – before you know of it, you’re loading half the database because you wanted this one customer…
Why use interfaces? Isn’t that needless complexity? How about YAGNI?
Yes, at this point, interfaces are overkill – I jumped the gun at this point (they will be needed later), but interfaces are much easier to discuss instead of jumbling around in implementations which may be littered with all kinds of implementation details.
Why the IAggregateRoot interface and the methods in it?
Basically the IAggregateRoot interface is mostly for my convinience. At this point it is not really needed, but I do not want to change the interfaces later on (the final solution is built…), so, there are a few methods that are a bit ahead of time. Also, adherring to DRY (Don’t Repeat Yourself) is one of the most important code practices in my book, so that will shine through in the solution.
In part 2, I’ll discuss the implementations of the above interfaces and the reasoning behind it.
About the series:
The solutions introduced here are not entry-level material. You should understand generics and basic patterns to fully utilize the material presented. The solution represents about 3 years of experience working with one ERP-solution. Some of the choices taken are heavily influenced from the large-scale solution and may seem overkill for the simple examples here, but the idea is to show the end-result of my experience and hopefully you as the reader will at least see a new way of doing things. The solution is built in .Net 4.0 with heavy use of WPF’s capabilities. Here you can find the source-code for the series. You are free to use this code for any purpose, but it is presented as-is with no guarantees. Enjoy!
Having started on the endeavour it is to create an ERP system (this time for real), I’ve put a lot of thought into the structure of the project. Having worked on our solution for transport for nearly 3 years has of course given me a lot of insights into what does not work, but also a lot of things that really works for me. Just to outline what I’ve tried during these three years:
CSLA – it sounded good when we started out – having all logic for persistence, validation etc. defined in base classes sounded too good to be true. It was. As the domain grew, it became impossible to follow what was happening. On top of this, there was a lot of backwards compatibility that was polluting the API. In the end we ended up discarding the idea – and going more light-weight.
Enter our first from-the-ground-up domain model with SubSonic as our ORM. All domain classes were encapsulated, handling all internal logic including UI-logic. Having only two developers, we constantly had problems doing things the right way – having odd work-arounds to get objects behaving correctly (no UI-updating when getting the object from the database was one of the major problems). We hired a guy to show us how it was done…
Enter our current solution: We use NHibernate for persistence, have repositories/factories create objects for us – separated all UI-logic out into ViewModels, started using Presentation models for semi-lazy loading. I’m quite happy with our current structure – although there are some mistakes that need to be refactored out. Some of the domain objects have too much responsibility and are way too heavy when loading.
So, why not just use the current structure for the ERP solution? Well, much of it is going in as-is. There is one major difference, though. The ERP-solution is going to run on WCF-services instead of a direct link to a database. This means that as much as I would like it, domain objects are not going to be loaded directly from the database, but rather from Data Transfer Objects (DTO). What we’re going to do is this:
For a common scenario, we need to list Orders and then be able to pick one and edit it:
1. We send a paged query for Orders satisfying our conditions to the WCF-service.
2. We show the DTO’s directly – all DTO’s are light-weight and only represent the data of the current entity (no journal, no vat-account etc. – we only have the Ids to find it later)
3. The user selects one order for editing – we use a factory (we call it entity-creator as we have a factory already) to convert the DTO into a full-fledged IOrder (the domain object with all the logic). During this, everything needed will be loaded – the Partner that should recieve the order, VAT-codes, Currency and so on.
4. We wrap the IOrder into a ViewModel that will give us a rich editing experience with nice combo-boxes, nice graphics and on-the-fly updates with totals and so on.
5. The user now makes his edits – the order is continually validated (it is responsible for this).
6. The valid IOrder is sent to the repository for saving – here it is converted into a DTO again and sent to the WCF service which again converts it into an IOrder and checks everything is still valid and is saved to the DB.
7. The list of Orders is updated to reflect the changes.
This may seem like a lot of steps for something this simple. We reached this model after a number of discussions and try-outs. This solution will offer some benefits:
* It is extremely performant – we only load enough data to enable the user to make decisions.
* It is extremely flexible – we can use scalar properties on the DTO that does not exist in the domain – we can even use views to populate it handing the calculation to the database server.
* We enable a rich UI when editing as the ViewModels will be very responsive and can show consequences before actually committing it.
* All functionality when it comes to business logic is portable – everyone can make use of the API as it exists far below the UI-components.
* Designed for testing. All components are very small and encapsulated. SRP was one of the major priorities when coming up with this model.
There is a few drawbacks, though:
* Complexity. It goes without saying that developers will need to keep a clear head as there will be up to 25 classes into play for an average Order view to work.
* The ViewModels will have a tendency to grow a lot.
ViewModels… well, it is my pain-point. All the ‘junk’-code seems to end up here… It is far nicer than bloaty code-behinds for sure, but it seems to me that some of the UI logic tends to be a lot of boxing and unboxing – scores of If’s and maybe’s. And of course, the master of spaghetti-code: drag and drop. I will try something ‘new’ here (as in: new for me). I’ll try to split the responsibilities into a few groups and dedicate a class to each.
First candidate is Drag and Drop – here the problem is: What to put in code-behind and what to put into DragAndDropHelperClassYetToBeNamed (DAD). I’ve learned that attaching event-listeners directly to the view is a bad idea. Instead the DAD will throw Actions, Functions and Delegates at the view, and let the view decide when to use which. This will allow me to re-use functionality for different events (like when double-clicking and left-clicking-while-doing-the-hokey-pokey should do the same) – one drawback is that the view will need to know enough of the domain, to do simple deciding.
Second candidate is ICommands – many commands need to be re-used anyway, so they might as well reside in reusable classes. Almost all commands will be relayed commands with Predicates and Actions. Almost all ViewMdodels will have a Commands-property where functionality available for key-presses, menus and buttons will reside.
ValueConverters are already not in the ViewModel as they need to be reused – and thus reside in ResourceDictionaries. This leaves showing data, doing UI-updates and delegating requests to the domain/repositories. This will have to do for now… Now to actually implement it… Don’t get me wrong, I think ViewModels are a fantastic idea – but they can become unwieldy if attention is not kept.
« Previous Page — « Forrige indlæg« Previous Page · Next Page »Næste indlæg » — Next Page »