3

This might be silly, but: let's say I want to delete a Book from the repo. Should I:

  • deleteBook(bookId) - send the ID since in the most of the time, the ID is passed from the UI

or

  • deleteBook(Book) - but then I need to fetch the book first? And this would be just a shortcut for above: deleteBook(book.getId()).

My guts tells me both should exist. Any wisdom on this?

5
  • 1
    Can you think of a use-case in which deleteBook(Book) is needed?
    – Allmighty
    Commented Oct 30, 2014 at 10:22
  • For example: "delete all old books". So I would get list of old books (e.g. published year < 1950) and then for each book I can call deleteBook(Book) - although that would be just a shortcut for: deleteBook(book.getId())
    – lawpert
    Commented Oct 30, 2014 at 10:35
  • Then there's your answer. Do keep in mind that you would first need to fetch all those books, create the objects, loop 'em all, just to delete those you just fetched and created. That use-case would get a 1 on my 1-10 efficiency scale.
    – Allmighty
    Commented Oct 30, 2014 at 10:49
  • Thats just an sample you asked for:) Sure, I would instead delete old books with one query. But, in that case, where would you put this method, if not in the repo?
    – lawpert
    Commented Oct 30, 2014 at 13:18
  • Also @Adimeus you can image some more complicated process of processing books and determining which one to delete.
    – lawpert
    Commented Oct 30, 2014 at 19:07

2 Answers 2

1

Repositories are fist-class citizens in the domain model, so they should operate in terms of ubiquitous language using entities and value objects. Therefore you should avoid naming repository methods after CRUD operations and using database table IDs (primary keys) to handle your entities. Instead you should focus on your business vocabulary and try to derive important concepts. Lots of times aggregate roots have a clear identity in the problem domain and it's a common practice in the DDD world to model such identity as a value object.

Going back to books. Not sure about your domain model but maybe you could use ISBN or similar as an ID for your books. Or you might want to use a combination of ISBN and some other identifier generated by your system. Anyways, I think this concept of a book identity deserves to be a part of your model as a value object.

Depending on your needs this is what your repository might look like

public interface BookRepository {
    public Book find(ISBN isbn);
    public Book find(BookID id);

    public void store(Book book);

    public void remove(ISBN isbn);
    public void remove(BookID id);
    public void remove(Book book);
}
2
  • Ok, got it. But some entities simple does not have any 'natural' key. For example, a Poll or BlogEntry does not have any natural id. So would you still have PollId in this case or just use long as id?
    – lawpert
    Commented Oct 30, 2014 at 19:16
  • 1
    Still think you should make it a value object whether it's natural or not. Referencing a domain entity by a number of type long doesn't make sense. What would you answer if I ask what this number is and what validation rules it has? A primary key in the DB, 8 bytes? I don't think so. You'd probably say it's a global unique identifier and it should be a positive number. This sounds more like a domain concept and business logic, so why not model it as a domain object and encapsulating the validation logic. Also read this.
    – zafarkhaja
    Commented Oct 31, 2014 at 6:45
0

I have always found it useful for the repository to track the entities it hydrates along with their primary key values in such a way that it stays with the context of the application. For instance, in your situation, I may have something like this on my BookRepository.

public class SqlBookRepository : IBookRepository
{
    public static List<KeyValuePair<string, object>> BookReferences = new List<KeyValuePair<string, object>>();

    public Book this[ISBN isbn]
    {
        get
        {
            if (!BookReferences.Any(b => b.Value.ISBN == isbn))
            {
                DBBook bookWithISBN = //TODO:  Call db and get the book.       
                Book book = HydrateBook(bookWithISBN);
                BookReferences.Add(new KeyValuePair(DBBook.ID, book));
                return book;
            }
            else
                return BookReferences.Single(b => b.ISBN == isbn);
        }
    }

    public bool Remove(Book book)
    {
        if (!BookReferences.Any(b => b.Value == book))
            return false;

        object bookId = BookReferences.Where(b => b.Value == book).Key;
        DataContext.Books.Remove(bookId);
    }
}

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.