2

The biggest difficulty I am having is finding responsibilities of each object identified by me in the system or so called problem space. I am positing a very simplified description of a book reader.

This time I thought like which actions can be performed on an object and that action became the method on that object, later who is going to perform those action became the parameter of the message/method but I am not sure because in that way every message would have something as a parameter. I would like to constraint the discussion to this level so that I could get clear discussion and answers.

/*
A user should be able to search a book from a library
bookmark it, favorite it or rate it.

A Book
  1. Can be favorited  (by whom?)
  2. Can be bookmarked (by whom?)
  3. Can be rated      (by whom?)
  4. Can be added to a library?
  5. Can be removed from a library?

A Library
  1. Can be Searched
  2. Can be modified?
    a. Add book
    b. Remove book

Relationship:

A library can have many book (o2m)
A library can have many users and an user can subscribe to many libraries(m2m)
A user can rent many books (o2m) needs a new model (rent)?
*/

interface Book {
  void bookmark(User user);
  void favorite(User user);
  void rate(User user);
}

interface Library {
  List<Book> search();
  int add();
  boolean remove();
  List<Book> all();
}

public class Main {
  public static void main(String... args) {
    System.out.println("Starting application");
  }
}

This is the high level view and I would like to know if this approach is correct?

5
  • If that is your biggest difficulty, then I would say you are pretty fortunate.
    – user251748
    Sep 28, 2017 at 16:31
  • Why would a book care who bookmarks, favorites or rates it?
    – Dunk
    Sep 29, 2017 at 17:53
  • @Dunk I don't know that's why I asked the question.
    – CodeYogi
    Sep 29, 2017 at 18:06
  • @CodeYogi - I suggest you read up on use-cases. They'll help identify classes and responsibilities. e.g. The user selects a book. The user adds the selected book to their favorites. This allows you to identify user, book AND favorites as good potential classes. User is basically your UI. It needs to provide a means to initiate selecting a book and adding it to favorites. Book needs to be uniquely identified and possibly be selectable. Favorites will just be a collection of books. The "hidden" class that coordinates the processing is the use-case. I tend to call them operations.
    – Dunk
    Sep 29, 2017 at 18:34

3 Answers 3

3

There is no one right way — there are multiple ways that would not be wrong. Especially when you have many-to-many relationships, like favorites that relate books to users.

When it comes to identifying responsibilities, I'll often consider — at the domain level, in terms of domain concepts and relationship — who owns the information/knowledge, which describes, preserves, and needs to be modified. It is the owner of that state who has the responsibility to hold, hand out, and update. So, I'd look to essential state to help identify responsibilities and their ownership.

Looking toward the domain and model for the most natural domain concepts: to me, it would be more natural that a user owns their list of of favorites, rather than a book owning a list of users who have favorite'd. So, in OOP, I'd model action (fav'ing and unfav'ing) as user object actions rather than book object actions.

However, you may find your application has the need to not just traverse from users to favorite books, but to also traverse from book to the list of users who have favorite'd, and if so, you may want such a list in the book object. No matter how useful, in my mind, this would constitute derived data rather than essential data, so I would probably not support update-actions/commands from that direction (e.g. from the book object).

(In persistence we are likely to model a (first-class) relation table that supports traversal in both direction.)

As far as bookmarks and ratings, I think these are fundamental domain concepts and merit their own entities and associations (and actions).

In collections, like the library, usually we let an entity representing the collection manage the admitting and removing of members rather than having the member add itself to the collection. This is because the responsibility for preserving and modifying the state associated with the relationship (collection membership) more naturally rests with the collection rather than the member.

7
  • Generally its said that we must think about the behaviour of the objects first or message passing so I thought it masked sense to pass a favorite message to a book rather than user.
    – CodeYogi
    Sep 28, 2017 at 6:53
  • Ok, and what i'm saying is that sometimes, to identify where a responsibility belongs (a command or update action), we can consider the domain information (state) and the ownership of that knowledge. Managing business/domain information/knowledge/relationships is a responsibility. So, the owner of the business information is a good candidate for collecting and modifying that information, and thus providing the methods for such. From this perspective, users are much more the owners of favorites information than books are of being fav'ed. So, it makes more sense to me that a user fav's book.
    – Erik Eidt
    Sep 28, 2017 at 15:55
  • In the this domain, the book is just a passive entity with virtually no responsibilities, whereas the user is an active, information owning entity.
    – Erik Eidt
    Sep 28, 2017 at 15:59
  • Also, I agree that there is no right way but there are some wrong ways which should be avoided.
    – CodeYogi
    Sep 29, 2017 at 18:07
  • So, basically from your answer I can infer that first I need to understand relationship between the objects first so, here the user, book relationship is many to many hence user can be responsible for managing that relation. Now, what if I want to add the feature of renting the book? the book cannot be rented to many users at the same time but different copies to different users so, does this make it one to many relationship between user and books?
    – CodeYogi
    Oct 19, 2017 at 13:53
2

I start decomposing my domain with a design story. What is my domain about? What's important? What are the primary concepts? What actions should be made (without identifying by whom at the moment)? What are the borders of my domain, what am I modeling and what am I not modeling?

After that I identify the main concepts that form my domain. Probably some prominent relations could be illustrated on a semantic net. So we come up with candidate objects. They should have some clear responsibilities to be useful.

Then I come with use cases. My candidate objects apparently take part in them. That's how I can have an idea what responsibility should every object have.

Things I keep in mind while modeling my problems space:

  • Objects are active and smart. They possess all resources they need to implement their responsibilities.
  • Responsibilities should be stated in an active voice. It's a consequence of a previous statement.

So talking about your domain.

Design story

I'm modeling a library. Any person can visit it. After registration, he or she should be able to search a book from a library, bookmark it, favorite it or rate it.

So from this design story I make a conclusion that I'm not modeling a domain of new books receipts. It's apparently a separate sub-domain with its own responsibilities. It represents a different business-capability, in other words.

Candidate objects

A library, a user, a book.

Use cases

A user searches for a book.

It might seem that it's a user's responsibility. But object thinking implies objects to be active and encapsulated. A user can not dig in library's internals. The library is responsible for information it possesses. It correlates with GRASP's information expert concept. So if a user wants to search a book, he should ask a library.

A user bookmarks a book.

A book might not know anything that it's bookmarked by somebody. It's not its business. Only a user cares that he or she bookmarked some book. So it's definitely a user's responsibility.

... and so on.

Code

interface User
{
    public function bookmark(Book $book);

    public function addToFavourites(Book $book);

    public function rate(Book $book);
}

interface Library
{
    public function search(Book $book);
}

Further thoughts

Chances are that every user's action -- adding to favourites, or bookmarking, or rating, ends up with its own concept with their own responsibilities. So the implementation could be the following:

class Bookmark
{
    private $book;
    private $user;

    public function __construct(Book $book, User $user)
    {
        $this->book = $book;
        $this->user = $user;
    }

    public function highlight()
    {
        // ...
    }
}

class Rate
{
    private $book;
    private $user;

    public function __construct(Book $book, User $user)
    {
        $this->book = $book;
        $this->user = $user;
    }

    public function discard()
    {
        // ...
    }
}

This concepts could be operated upon in application's entry point, so probably the User could be discarded of this responsibilities.

And here is another example of the ways to decompose a domain.

1

From a high level view you seem to have modeled correctly this business, however there is a rule that you haven't respected: you should reference other Aggregates only by ID. This rules will help you separate the Aggregates better because you can't access the other Aggregate properties from an Aggregate.

So, your code should look something like this:

interface Book { 
    void bookmark(UserId userId); 
    void favorite(UserId userId); 
    void rate(UserId userId); 
} 
interface Library { 
    //List<Book> search(); - // this should be a domain service
    void addBook(BookId bookId); // CQS - command
    void removeBook(); // CQS - command
    List<BookId> allBooks(); 
} 

Searching the library should be a domain service's responsibility because it involves multiple Aggregates and it is used only for reading.

You should also follow the CQS pattern.

2
  • Hmm, so one important take away from your answer is you should reference other Aggregates only by ID.
    – CodeYogi
    Oct 19, 2017 at 13:55
  • @CodeYogi The most important domain architecture advice Oct 19, 2017 at 14:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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