0

I have a question about keeping track of objects in different layers of a software application. In my application, I have objects in the domain layer (e.g. LineShape) that are used to represent business entities, and I have corresponding objects in the presentation layer (e.g. System.Windows.Shapes.Line) that are used to display these entities on the screen.

My question is, how do I keep the correspondence between the domain objects and the presentation objects, so that I can identify which domain object is represented by a given presentation object?

For example, if the user clicks on a System.Windows.Shapes.Line in the user interface, how can I determine which LineShape in the domain layer this object represents?

I have thought of a few potential solutions, but none of them seem ideal, especially for larger and more complex object models.

  1. One solution is to use a dictionary that maps presentation objects to domain objects. In this case, when the user clicks on a System.Windows.Shapes.Line, I could look up the corresponding LineShape in the dictionary.
  2. Another solution is to use an ID for both the presentation and domain objects. This approach has the advantage of being simple, but it seems strange to me to use IDs for every object in a domain-driven design, as IDs are typically used only for entities.

Are there any best practices or established patterns for solving this problem?

enter image description here

Vahid
  • 5,144
  • 13
  • 70
  • 146
  • You would do the drawing by means of an ItemsControl (or a derived control like ListBox), which has its ItemsSource property bound to a collection of domain objects. The presentation objects would be created by DataTemplates, either auto-selected by their DataType, or by the ItemsControl ItemTemplate or ItemTemplateSelector. Like e.g. this: https://stackoverflow.com/a/40190793/1136211 – Clemens Sep 09 '20 at 07:43
  • @Clemens Thank you Clemens, I also thought of that one, making it similar to displaying a list of strings. Of course, the example I provided above is simplified for the question. In a more complex situation, I cannot bind to an ItemsSource, the graphics API can be different than WPF for example, which does not allow ItemsSource. Or there are cases where DTOs come in between the Presentation and Domain. What about these cases? How do I know then the DTO that is now in my ItemsSource belongs to which Domain object? Thanks you. – Vahid Sep 09 '20 at 07:51
  • @Clemens In the link you provided, how ShapeData is linked to the actually Shape class in Domain? Let's say that the user select a Shape in Presentation and deletes it. Now from your approach I know that it corresponds to which ShapeData, but how do I know to which Shape in Domain this ShapeData is linked? Should again the ShapeData reference the actual Shape in Domain? Or have some Id? or...? – Vahid Sep 09 '20 at 07:58
  • "Should again the ShapeData reference the actual Shape in Domain?"- yes, that is the usual reference between view model and model. – Clemens Sep 09 '20 at 08:01
  • @Clemens Thanks for the clarification. If there is a DTO between ViewModels and Models, should then ViewModels reference DTOs and then do these DTOs reference Models? This part seems odd to me because I have not seen any where that DTOs reference Models. How do we find the link then? – Vahid Sep 09 '20 at 08:03

2 Answers2

1

Use an Id for objects both in Presentations and Domain. This sounds a bit strange to me, because as far as I am aware, we are not supposed to use Id for every object in DDD, just for entities.

Perhaps you would be looking at an address rather than an ID. Even values must somehow be addressed so they can be replaced. Such an address may be a class property's name, an array's index, etc.

If you see the whole drawing as a collection of shape values then you could always use the shape's index as the address allowing to replace that specific shape value in the collection.

However, note that the address is only valid as long as you are working against the expected drawing's version.

Furthermore, you could also use the combination of all shape's properties to identify a given shape to modify. If you have two identical shapes (same shape, position, layer, etc.), does it matter which one you re-shape? The final drawing would look exactly the same.

In the end if it makes your life easier to model shapes as entities and given them an ID then perhaps that's the right model too even though you may not care about the specific shape's entire lifecycle.

Finally, note that if shapes are values then you can't possibly keep a reference to them in view models since values are immutable. Also, if shapes are entities, but the drawing is the Aggregate Root then shape entities shouldn't be accessible outside the root. Therefore it most likely only makes sense to reference a domain's Shape in the view model if it's an AR (unless you violate the visibility rule, but still enforce invariants through root-handled events). Also note that address/ID references may be the only option if your domain model lives on another tier.

plalx
  • 42,889
  • 6
  • 74
  • 90
0

You've asked a somewhat broad question, so I will give what I think is a correct but general answer.

In WPF, one way or another, the on-screen controls will have a DataContext. The DataContext generally speaking should be the object you want to get a hold of.

This should be the case more or less regardless of what method you use to populate all the screen controls.

It may suit your program to create special viewmodel classes which bridge between your "native" data objects, in which case those viewmodel classes will be set as the DataContext objects. If you don't need the viewmodel then the native data objects will play that role.

If you are using events, then in the code-behind for the event you can directly access the DataContext property, cast it to the expected type, and off you go.

If you are using commands then generally they are part of the viewmodel object in the first place, so they can just act directly on the object which owns them.

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81