I want to encapsulate all the DB code of an app inside a set of repositories. I
do not want those repositories to leak DB implementation details, such as the DB
type, driver, etc;
One way to do that is to define traits for the repositories, then each
repository can hold a reference (or own) a DB connection.
struct Repo {
c: Rc<RefCell<Connection>>
}
So far so good, but this approach makes it impossible to have a transaction that
spans multiple method calls or multiple repositories, because the repositories
does not know anything about the transactions.
In a blog I've read (which cannot find right now) I've seen that they handled
the problem by passing the connection to each method call, instead of holding it
as a member of the repository structure.
struct Repo;
impl Repo {
fn some_method(&self, tx: &Transaction, params...) -> Result<()> {
//
}
}
I really don't like that because now my repository leaks the DB implementation
details. So, I thought of doing something like that:
struct Repo<'l> {
c: Rc<RefCell<Transaction<'l>>>
}
The issue here is that the Rusqlite's transaction borrows the connection object,
so in order for this to work someone has to own the Connection
object itself:
struct UnitOfWork {
c: Rc<RefCell<Connection>>,
tx: Rc<RefCell<Transaction>>
}
impl UnitOfWork {
fn new_repo() -> Repo {
Repo { c: tx.clone() }
}
fn complete(self) {
tx.borrow_mut().commit();
}
}
The issue here is that the UnitOfWork
structure is now a self-referential
structure, which I really don't like and want to avoid.
I can avoid using the transaction()
method and type, but then I have to
implement myself what those are doing in my code.
So how should I model that ? I want to encapsulate the interactions of the DB
without leaking DB impl details.
PS: all code is pseudo-rust