-2
class DomainEvent {

}

and then I have different events with following structure:

class SomeEvent extend DomainEvent {
   private UUID orderId;
   private UUID orderlineId;
   private UUID productId;
}

Now lets say that I have 20 such events and most of them have orderId + orderLineID as attributes som have in addition productID.

There are also some events that are not oriented around the Order , so they may have differnt fields.

In the begining I though mayve I can create

abstract AbstractOrderEvent extends DomainEvent {
   private UUIID orderId;
}

and then

abstract AbstractOrderLineEvent extends AbstractOrderEvent {
   private UUID orderlineId;
}

and then

abstract AbstractProductEvent extends AbstractOrderLineEvent {}

But it looks to me plain ugly! Maybe my own taste.

Then I thought maybe I can do the same with interfaces. But to be honest it does not look much better.

I also thought to have something like

DomainEvent<T extends Subject> {
   
    T  subject ( and then the subject to has different attributes)

}

and then I can have something like

OrderCompleytedEvent extends DomainEvent {

}

How can I model this case ?

Pesho
  • 530
  • 4
  • 9
  • 4
    What are you trying to _achieve_ with this abstraction? Do you want to represent some _relationship_ between the events? To reduce copy-and-paste of _defining_ the events? To reduce copy-and-paste of _handling_ the events? All of these might suggest different designs. – IMSoP Feb 15 '21 at 19:25
  • Yes reduce Copy paste mostly. Same methods being pasted around again and again. A good abstraction can eliminate this. At the same time I kind of dislike the inheritance aproach not sure if it is just me or it is ugly :) – Pesho Feb 15 '21 at 19:26
  • I might want to recommend some kind of code generation. You could define metadata about the events and then rest of the code generated by some kind of tool. But which tool depends on your language or framework. – Euphoric Feb 15 '21 at 19:32
  • @Euphoric if I just use code generation tool and no other form of abstraction still the copy paste code will remain. I still need to generalize the events that are based on Order from the events that are based on Product lets say. So code generation alone wont do it. – Pesho Feb 15 '21 at 19:47
  • I think I don't understand your problem unless you go more into detail which code is being copy-pasted. – Euphoric Feb 15 '21 at 19:51
  • F.ex. Everytime we need to add an event and perform an action for refreshings some data around OrderId OrderlineId and ProductlineId. Right now one event results on one or more methods registered under the three refreshers. Lets put it this way which is unnessesary as it does not matter which event is it as long as it contains orderId and/or orderlineId and/or productilneId – Pesho Feb 15 '21 at 20:07
  • Right, so there **is** a relationship you want to represent between those events. Inheritance seems a very reasonable choice; but it's hard to answer your judgement that it "looks ugly" without understanding what you mean by that. – IMSoP Feb 16 '21 at 07:53
  • @IMSoP lets say that combination of Order, orderline, productline correcsponds to particular product. It does not make sense for a product to be a subclass of Order. It is just plain incorrect even though possible. – Pesho Feb 16 '21 at 10:43
  • @Pesho Right, this might be a clue as to where to look: right now, that concept of "product" isn't visible in the design at all. It should probably exist as its own entity in the system, and then a product-related event wouldn't have an OrderId at all. I'd also question the use of UUID fields, rather than referencing actual entities; that feels like a representation needed for serialization not what you'd want to process the events themselves. – IMSoP Feb 16 '21 at 11:30
  • @IMSoP that depends on the case in the particular case the product is part of an Order. (Invoicing) – Pesho Feb 16 '21 at 11:44
  • 2
    @Pesho Well, whatever the relationship is, your application should be modelling it - not just on the events, but as actual data entities. I think that's why your events feel "ugly" - they're completely divorced from any model of your domain. – IMSoP Feb 16 '21 at 13:05
  • @IMSoP "completly divorced from any model" that was a good one:) I think I can agree with that. – Pesho Feb 16 '21 at 16:25

2 Answers2

1

There are three different sets of relationships to represent here:

  • The relationships among the domain entities. If an "Order Line" is always part of a specific "Order", you should represent that directly, not by always passing them as a pair. Similarly, if an "Order Line" has a specific "Product" attached to it, you shouldn't need to pass that Product around specifically, it's a dependent field of the Order Line. Most of these relationships will be composition, not inheritance. This should be your primary focus, since it will affect all areas of the application, not just the event system.
  • The relationships among the events. For instance, you might have a service that's interested in all events that affect product inventory, and another service that's interested in all events that involve payments. Those categories of events might expose similar behaviours, e.g. getting the total amount of money transacted. That might well be a directed graph rather than a tree, so depending on the language you're using could be modelled with multiple inheritance, interfaces, traits, etc.
  • The relationships between the two sets of objects. Your events are currently very thin, holding only UUIDs; this might be a reasonable way to store and transmit them, but your customers aren't buying UUIDs, they're buying products, so that's what your application should be modelling. An "Order Line Created Event" could contain a single "Order Line" object, from which all its details can be obtained. An "Order Line Updated" event could contain separate "before" and "after" Order Line objects.
IMSoP
  • 5,722
  • 1
  • 21
  • 26
1

IMSoP is correct in identifying that abstraction without a purpose is meaningless, so we need to know what you intend to achieve. To quote an answer I wrote in the past on the same topic, abstraction without a concrete goal is turtles all the way down.

Your response:

Yes reduce Copy paste mostly. Same methods being pasted around again and again. A good abstraction can eliminate this.

The main issue here is that just because two things are structurally the same, does not mean that they (or their similarities) are actually the same thing.

Reducing copy/paste by creating shared logic is only applicable when the duplicate instances share the same lifecycle, i.e. changing one inherently means changing the other.

While you may have several events that happen to have some similar properties; do those events share an actual life cycle? My guess is not, since events tend to be independently defined.
This principle extends beyond events, well into general OOP. Classes exist because they have a responsibility, and as clean code dictates each class should have only one responsibility. Two responsibilities therefore need to be represented by two classes.

This remains correct even if these classes happen to be structurally equal to one another.

A simple example here is a domain object, data entity and (public) DTO. These three classes could all have the exact same set of properties, but they each have their own unique purpose, and they should therefore not be lumped into a single class.

When trying to refactor code, you have to distinguish coincidence from correlation. If you spot a similarity, and there is a correlation here (e.g. if one changes, the other must change too), then there's reason to abstract. But if it is coincidence, i.e. they are completely unrelated except that they happen to look the same for different reasons, then abstraction would actually be introducing problems instead of solving them.

So I'd advise against trying to merge these events into some shared logic just because they happen to be structurally similar at the moment.

Flater
  • 44,596
  • 8
  • 88
  • 122