3

I am trying to create a social network app. When I started I had proper domain models for Feeds, Activities, Author etc. with all the behaviour encapsulated within the Objects. I had the luxury of ef-core for easy change tracking and infrastructure abstraction.

But then I started using GetStream.io to get feeds for my application. And since then my application is back to having all the logic in Service layer.

Can someone suggest me how to improve this and use DDD in this context.

3
  • 2
    What do you mean by "feeds"? Can you elaborate? Jun 2, 2020 at 16:54
  • feeds mean news feed Jun 3, 2020 at 4:47
  • For downvotes to the question, It will help me improve it, if someone could explain the reasons behind it. Jun 3, 2020 at 4:48

2 Answers 2

5

And since then my application is back to having all the logic in Service layer.

There are two things that you can do here.

The key idea is that "retrieving data" is a completely different concern from "processing" data. Retrieving data needs IO, so that concern is normally managed by your application components. Processing data is a problem for your domain logic.

So we use application code to get the data, then pass the in-memory representations of that data to the domain code.

One approach is to treat "retrieve the data" as something analogous to a domain service. So the domain model defines some interface that describes an implementation agnostic retrieve of the data, and the application implements an instance of that interface, wired up to the correct IO mechanism. For use cases that need the remote data, the interface of the aggregate root includes in its target method(s) an argument of the appropriate interface.

In effect you "dependency inject" the application's implementation of the interface into the function.

An alternative approach is to have the domain model ask for the data it needs, which the application service retrieves and passes to the domain model as a value. So all of the IO concern lives in the application component, and the aggregate root acts like a little state machine that tells the application what it wants.

switch (root.state()) {
    case I_NEED_DATA:
        data = getTheData(root.args())
        root.onData(data)
        break
    //....
}

See Cory Benfield's talk on Building Protocol Libraries to get a broader sense for how this might work.

If the distinction between the two styles in unclear, you might reflect on how you would write an isolated test for your domain code in each case. Using the first pattern, you would end up passing a test double (aka a "mock") to the logic under test. In the second pattern, you would end up passing plain old data (because all of the I/O that you would otherwise need to mock away is invoked by the application code, not the domain code).

1
  • great, your answer started me to think. One Idea that came to me now is that I can extend the Aggregate root class in my service layer to implement proxies for fetching data from API. Similar to the second approach but with new derived class. Another problem in the same context will be that we will end up consuming almost double the memory at any point of time (return from API+ Our domain objects) . But that can be taken care once the design is in place. Jun 3, 2020 at 6:25
1

Great that you already had the behavior in the objects! I'm not really sure why changing the source of the data made any difference to your design though. The design should reflect, well, the domain, not where the data comes from.

So suppose, you had:

interface Feed {
   void Subscribe();
}

That does not change when the source of data changes, that only changes when the "domain" itself changes. Sure, the implementation does change, but there can be a DatabaseFeed and a GetStreamIOFeed or whatever.

Separating the storage from the business logic with data records is ironically exactly what makes the design brittle.

1
  • GetStream had its own aggregate. And each query had to go through it. Like StreamAuthor.GetFeed(). Same happens when I want to write the data. StreamAuthor.PostActicity(). Also constructing activity has its own concepts to be learnt from GetStream. A was able to keep my interface for the Application Services but my old domain object became completely unusable. And now since I don't own most of the domain concepts I am stuck with lot of application services querying directly for Stream Apis. ACL could have helped, I don't know. Jun 3, 2020 at 17:28

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.