herbsjs / herbs

A domain-first framework. Code your domain and your infrastructure will follow
https://herbsjs.org/
MIT License
427 stars 14 forks source link

New Glue - Herbs2memcached #52

Open jhomarolo opened 1 year ago

jhomarolo commented 1 year ago

I'm suggesting a new herbs glue for storing and fetching entities in an in-memory database

Initially, the idea is to build the glue for Memcached and then replicate it for Redis as well.

Here is the first (and not optimized) alpha version: https://github.com/jhomarolo/herbs2memcached/tree/main/testdb

I thought about replicating the methods (set, setmulti, get, getmulti) and still doing some abstractions like getAll and getbyId.

Additionally, I'm trying to resolve a known timeout issue in the Memcached library (https://github.com/3rd-Eden/memcached/pull/199)

Feedbacks are welcome

dalssoft commented 1 year ago

Here are some thoughts.

Before starting going deep into the code, I would like to understand the use case. There are two scenarios that I can think of where memcached could be used:

no database, just memcached

In this case, memcached is the main repository, no other database is used. The approach you are presenting is good for this scenario. However, I can't think of a real use case for this scenario, where all the data is stored in a volatile memory, with no persistence. Probably there are some, but it is more a exception. In this case would be worth the effort?

database + memcached

Here, the memcached is used as a cache for the database.

In this case, the glue should be a cache adapter (or something like that [1]), not a repository.

The adapter wouldn't need a data mapper to save the entity, the serialization is straightforward (JSON.stringify) and could use the metadata to set the key (entity IDs).

To fetch the entity, the adapter would need a data mapper to deserialize the a valid entity since it would not interact with the repository. And fallback to the repository if the entity is not in the cache.

The adapter should be able to work with any repository. This can be a challenge, since the adapter would need to know the the interface of different repositories (herbs2knex, herbs2mongo, etc).

// very simplified example
const User =
        entity('User', {
          id: id(String),
          nickname: field(String),
          password: field(String)
        })

const user = new User({ id: '1', nickname: 'john', password: '123' })

const userRepo = new UserRepo()
const userCache = new UserCache(userRepo)

await userCache.insert(user, { ttl: 1000 })
const userFromCache = await userCache.getById('1')

[1] If not a adapter, maybe a chain of responsibility of repositories, where the first handler is the cache and the second is the repository.

Does it make sense?