A basic principle of enhancement in Object Guidance is the separation of responsibilities.
See Disclaimer for more details.
Four responsibilities
A widely used design pattern identifies at least 4 responsibilities in solving this problem:
1) Entity
Object that represents the entity; in this case, a system user. It has the attributes and business behaviors of the entity.
2) Repository
Object that abstracts entity persistence and recovery.
It has CRUD methods, if needed, and other search methods.
Example:
class UsuarioRepo
Usuario[] usuariosAtivos()
The repository can also be generic, where a single object abstracts the persistence and recovery of all system entity types:
class Repositorio
T[] obtem<T>(CriteriosPesquisa)
But the Repository does not hold database structure information, nor does it know the specificities of the database being used. In other words, the repository does not know anything about the tables in the database and does not know how to do SQL commands.
To do your job, the repository uses the following objects (and after we talk about them, we'll go back to the repository).
3) Database Entity Mapping
Based on definitions in XML files or in metadata declared in the entity class, this object delivers a mapping between entities and tables and columns of the database.
This object has knowledge of the bank structure and its relationship to entities, but it also does not know how to do SQL commands.
4) Interaction with the database
Here we have one or more objects specialized in assembling SQL commands.
There can be one object or a set of objects handling the specificities of each supported database server.
There may also be an abstraction of this interaction with the database, especially if more than one database is supported.
Back to the Repository
As I said, the repository uses the other objects to do its job. For example:
class UsuarioRepo
Usuario[] usuariosAtivos()
{
var mapeamentoUsuario = mapeamento.get<Usuario>()
var sql = sqlBuilder.select(mapeamentoUsuario).criterio("ATIVO = 'S'")
return conexao.executeSelect<Usuario>(sql)
}
See that I've abstracted other responsibilities in this response, such as the connection object and objects that execute commands against the database.
ORM
If you implement this solution, you will have developed an ORM framework , but you can also use a market framework.
An ORM provides Entity / Database mapping capabilities and abstracts the connection to the database, building SQL commands, and executing these commands in the database.
The ORMs for Java, Ruby (on Rails *) and .Net are very simple to use and although complex, they do not add complexity to the project because this complexity is encapsulated within them.
Using an ORM, the code looks something like this:
@Entity("TAB_USUARIO")
class Usuario
@Column("LOGIN")
username
@Column("SENHA")
password
class UsuarioRepo
Usuario[] usuariosAtivos()
{
return orm.query<Usuario>("select u from Usuario u where u.ativo = true").result()
}
Note that this command is not database SQL native code, nor is it about tables, but about entity names according to their class. The ORM, in turn, handled the mapping, the specificities of the bank in question, connection pooling, etc.
There is still the option of using lambda and other patterns instead of writing the command as a string; and .NET still offers LINQ.
You can still choose not to encapsulate persistence and recovery in repositories , but instead write the queries each time you need them - as are queries against entities and not against native SQL against tables, you do not would be spreading database code by your application. I usually use repositories because of some features of my context.
Rails uses the ActiveRecord model, where persistence and retrieval behaviors belong to the entity and its class, respectively, rather than to a separate object. / p>
Conclusion
Separation of responsibilities can go far beyond writing SQL in a class other than the entity class.
You should make this separation according to the characteristics of your context.
For example, if software meets a complex need, separating responsibilities well can help prevent domain complexity from contaminating the code. The more complex the domain, the more it compensates for the separation of responsibilities, which helps to keep the code simpler (although this statement may be somewhat counter-intuitive).
Finally, you generally will not need to develop an ORM to help with separation of responsibilities as there are good frameworks available for that.