-1

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:

  1. 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
  2. User manually changes some parameters

    • Based on domain logic material and time needed to manufacture is calculated.
    • User sees the recalculated values
  3. Manually edits the calculated parameters

  4. 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) 
      { ... }
}
12
  • What is hindering you to update the domain object (ProductOrder) when the user changes "some parameters", but without without persisting the updated object?
    – Doc Brown
    Commented Aug 30, 2023 at 16:30
  • 2
    "when the domain logic is executed, how do I apply the changes back to viewmodel?" - not sure what's the source of the confusion, could you elaborate a bit more (and maybe edit the question itself, cause comments are transient)? There are different ways to do it, but, your call to the domain logic either returns something, or eventually produces an event or calls some callback, and you update the viewmodel based on that. Commented Aug 30, 2023 at 20:34
  • 2
    @Liero: how do you bring the calculated values into the view model first place? Why not use the same mechanics to bring the recalculated values there again?
    – Doc Brown
    Commented Aug 30, 2023 at 21:10
  • 1
    This question is nigh unanswerable with the current information. The plain answer is "however you've designed your interaction with the domain". There are myriad architectures and approaches here and it's impossible to write an answer that is somehow specific to your case yet covers the entire range of possibilities.
    – Flater
    Commented Aug 31, 2023 at 0:57
  • 1
    It depends on if you make the callback too UI-specific. If you have a structure that's like [presentation logic]-[application logic]-[domain logic], then such a callback would be required by a component in the application logic layer, but it would be passed in by something in the presentation layer. My answer here tries to convey in a simplified way how this might work in Clean Architecture, maybe it'll be useful, at least on a conceptual level. Commented Aug 31, 2023 at 6:43

1 Answer 1

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);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.