vendredi 19 février 2021

How to design API to persistence layer?

Imagine the situation. There's a table with say 20 fields, like:

| n/n | name |
|-----|------|
| 0   | id   |
| 1   | foo  |
| 2   | bar  |
| ... | ...  |
| 20  | baz  |

and you need to design CRUD API for it. There's nothing complicated with create() and delete() methods, but update(..) and find(...) are real pain.

From UI perspective user can change or request a search by any of those fields and in any combination: update(foo=x, baz=y) or find(foo=x, bar<=y).

There are two pattern for DAL: Repository and DAO.

  1. Repository says "threat API like a collection of objects", so it's usually looks like that:

    Repository {
      replace(Pojo o)
      find(PojoSpec spec)
    }
    

    find() is ok, but using replace(Pojo o) means that we lose info about what fields has been actually changed. We have to either update the whole object or select-compare-update only changed fields (so +1 request, please don't tell about premature optimization, it's an API design question). Both options are sub-optimal, because DB also doesn't care about comparing values. It just rewrites all fields specified in the SQL query. What if table has 100 fields? 200? Would you rewrite 100 fields if just one of them has been changed?

  2. DAO is not really a pattern, it's just some persistence-oriented code around domain object with no real restrictions. So, API like findByFooAndBarAndBaz() is quite usual. It also doesn't solve update() problem though.

So, what's left? We could just pass changed fields as key-value pairs, but it's ugly and not type safe: replace(Map<String, Object> o). Type-safety can be fixed buy creating a metamodel, but exposing metamodel classes to services layer looks like sort of code smell.

The question is: what is the common approach to handle situations like that? Are there another DAL patterns? Am I right about metamodel code smell?

Aucun commentaire:

Enregistrer un commentaire