1

Currently I'm working on a project that will interact with a database. Based on my research, I would like to develop a repository class which responsability is to write/extract entities to/from a database, as mentioned in Ivar Jacobson's book "Object-oriented Software Engineering: A Use Case Driven Approach" and further in Uncle bob's keynote Architecture the Lost Years (I also plan on implementing Interactor and Boundaries later on, but I'm starting with the database for now).

Entity Gateway Pattern

I would like to design an implementation that follow some simple rules I think are valid for the Repository class:

  • The name of the database is parametric, which means that each child of the Repository class must know the name of the database it is connecting to.
  • It should get the information for a IDbConnection from an factory, which will be injected as a singleton dependency during runtime.
  • It should expose methods for queries and commands, where the INSERT command should return the Entities Id, and other ones (i.e. UPDATE, DELETE) should return the number of rows affected.

To do such, I've implemented it using Dapper (a Micro ORM) on a PostgreSQL database this way: `

public abstract class Repository<TEntity> where TEntity : class, IEntity
{
    private readonly IDatabaseConnectionFactory databaseConnectionFactory;
    private readonly string databaseName;

    protected Repository(IDatabaseConnectionFactory databaseConnectionFactory, string databaseName)
    {
        this.databaseConnectionFactory = databaseConnectionFactory;
        this.databaseName = databaseName;
    }
    protected async Task<int> ExecuteCommandAsync(string command, object dynamicParameters)
    {
        using (IDbConnection databaseConnection = databaseConnectionFactory.GetDbConnection(databaseName))
            return await SqlMapper.ExecuteAsync(databaseConnection, command, dynamicParameters);
    }

    protected async Task<int> ExecuteInsertCommandAsync(string command, object dynamicParameters)
    {
        command = AppendReturnIdIfNotContains(command);
        using (IDbConnection databaseConnection = databaseConnectionFactory.GetDbConnection(databaseName))
            return await SqlMapper.ExecuteScalarAsync<int>(databaseConnection, command, dynamicParameters);
    }

    protected async Task<IEnumerable<TEntity>> ExecuteQueryAsync(string query, object dynamicParameters)
    {
        using (IDbConnection databaseConnection = databaseConnectionFactory.GetDbConnection(databaseName))
            return await SqlMapper.QueryAsync<TEntity>(databaseConnection, query, dynamicParameters);
    }

    private static string AppendReturnIdIfNotContains(string command)
    {
        if (!command.Contains(" RETURNING id"))
            command += " RETURNING id";
        return command;
    }
}

I've found the ExecuteCommandAsync and ExecuteInsertCommandAsync very similar to one another, which makes me think if i shouldn't make the INSERT, UPDATE and DELETE commands follow the first method, and if needed, run a query such as SELECT lastVal() for the id or SELECT @@ROWCOUNT for the number of affected rows, as it will also be complient on a CQRS point of view. Should I implement it or keep it the way it is?

5
  • Why are you not using the Entity Framework? – Ron Beyer Mar 14 at 16:06
  • I've found that the use of Entity Framework for this project is a bit of an overkill, it is a simple application that requires CRUD features, and also, I would like to write custom queries and class models that differ from the EF auto-generated code. – Victor Gazzinelli Mar 14 at 16:15
  • 1
    Is there any reason you want this? Is there any reason to do multiple abstractions? Is there any reason to have the entity abstraction even? If this is a simple application as you said, KISS says this is cruft. – Robert Bräutigam Mar 14 at 18:40
  • Yes, the reason why I want this is to understand better about the EIB architecture and it's usage on a real world application. The reason for multiple abstractions is to help when mocking interfaces to make unit tests in order to get feedback from the aplication. The reason for a Entity abstraction is to include a few controlish properites such as Id, Updated at and Deleted at (for logic exclusion). The application is simple regarding a Micro ORM x Full ORM standpoint, but it will have dozens of entities, and repositories for theses entities, so things can escalate very quickly. – Victor Gazzinelli Mar 14 at 20:37
  • I see the pratical reasons for Keeping things simple, and they really should be, but in this case I'll be using a 3-tier archtecture in order to prevent me ( or in this case, any developer participating in the project ) breaking some principals in order for code to "work". – Victor Gazzinelli Mar 14 at 20:41
0

While building it yourself is certainly a good way to learn; building a project that you want is best. You learn along the way.

However, in this case, the best way to learn about such a prolific topic is to stop reading sources that fawn about these things and start to learn about the flaws and criticisms.

Great thinkers like Uncle Bob, have brought many useful ideas to software development, but they are not infallible. You find out where they are wrong, but reading from reputable skeptics.

Here are some good skeptical sources

Now, you don't need to agree with all skepticism, but it's important that you are exposed to them, so you can make informed decisions in your professional software programming career.


My company is working on a draft new open standard for cutting through the hype of "software architecture". The best relevant starting point, is the comparison to microservices - https://colossal.gitbook.io/microprocess/comparisons/compared-to-microservices

It's possible to contain complexity within the data-layer entirely and fully decouple mutating processes, and thereby reduce the amount of host infrastructure, eliminate custom middleware code (where security bugs arise), and reduce technical debt.

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.