I have a rich client with quite complex domain logic and I want to keep the domain and presentation layer separated. How would you advice to intergrate the domain logic with the UI in this case.
I implemented in the domain objects and services in following fashion
public class ProductOrder
{
public double SomeParameter {get; private set;}
public IReadOnlyCollection<ManufactureTime> ManufactureTimeNorms { get; }
public IReadOnlyCollection<ProductOrderMaterial> MaterialItems { get; }
public void SetSomeParameter(double value)
{
SomeParameter = value;
RecalculateMaterials(); // modifies domain object
RecalculateTimeToManufacture();
RecalculatePrice();
}
}
Use case
From UX perspective, the workflow is:
User creates product order by selecting a product from calatog.
- Parameters are copied from catalog to order and based on domain logic material and time needed to manufacture is calculated.
- User sees the calculated values
User manually changes some parameters
- Based on domain logic material and time needed to manufacture is calculated.
- User sees the recalculated values
Manually edits the calculated parameters
User clicks save and the parameters and calculates values are persisted in database.
The problem:
Typically, there is a ViewModels are constructed from a domain objects, presented in the UI where user can make changes and when saving, the domain objects are updated.
However, this does not solve the problem of applying business rules and presenting modified domain object back in the UI. How to design two way interaction between viewmodels and the domain?
Since the business logic is fairly complex, I would like to avoid duplicating all business rules in ViewModel.
What I have:
I don't have anything yet, but I would start with this
public class ProductOrderToViewModelMapper
{
public void MapToViewModel(ProductOrder domainObject, ProductViewModel viewModel)
{ ... }
public void MapToDomainObject(ProductViewModel viewModel, ProductOrder domainObject)
{ ... }
}
1 Answer 1
Material, TimeToManufacture and Price should be just standard members of ProductOrder, and there should be a function ProductOrder.CalculateDerivedValues()
which recalculates them based on other parameters within the object.
Step 1 looks like this (for the sake of simplicity synchronously, the real code can by asynchronous / event driven):
productOrder= CreateProductOrderFromCatalog(...);
productOrder.CalculateDerivedValues();
mapper.MapToViewModel(productOrder, viewModel);
PresentViewModel(viewModel);
Now the viewmodel is presented to the user, user changes some parameters and the UI initiates an event which cause to happen this as step 2:
mapper.MapToDomainObjekt(viewModel, productOrder);
productOrder.CalculateDerivedValues();
mapper.MapToViewModel(productOrder, viewModel);
PresentViewModel(viewModel);
It does not matter that the mapper does not know which attributes have been recalculated and which not. Just map the data completely in both directions. Changed values get an update with the new values, unchanged data will stay the same (they will be assigned just the previous values).
Of course, for the unlikely case you need some optimization which updates only the derived values, you may add a function UpdateOnlyDerivedValues(productOrder, viewModel)
to the mapper and call that instead. But I would be extremely hesitant with that: though it does not duplicate the recalculation logic, it still duplicates the knowledge which values are derived, and which are not, which might become a source of errors.
Step 3 will then be the editing inside the view, and step 4 just
mapper.MapToDomainObjekt(viewModel, domainObject);
Persist(domainObject);
Explore related questions
See similar questions with these tags.
ProductOrder
) when the user changes "some parameters", but without without persisting the updated object?