I am writing applications in C++ using an sqlite database. Suppose I have a simple user system with a user database.
User Class
class User {
private:
void LoadFromDatabase(uint32_t id);
public:
std::string name;
uint32_t age;
void SayHello();
void SetAge(uint32_t age);
};
User Table
+-------+-----+
| Name | Age |
+-------+-----+
| Billy | 22 |
| Bob | 44 |
+-------+-----+
When getting data from the database, I like to store things in row objects so it's super easy to figure out what's in the row (otherwise its a pointer to a char array and you'd have to know what each column index is). This is where the redundancy problem comes in.
User Row
class UserRow {
public:
std::string name;
uint32_t age;
};
So now I have repeated the class properties between two classes. This is obviously a trivial example, but sometimes rows have 10 or more columns. One solution would be to get rid of UserRow and just pass around User, but the database shouldn't really know anything about User and be as dumb as possible. Another solution could be to have User.properties which is = to a UserRow, but that makes it less accessible.
What would be the best way to avoid these duplicate fields/classes?
2 Answers 2
Look at this approach :
class User {
private:
UserRow userRow;
void LoadFromDatabase(uint32_t id); // init userRow
public:
std::string GetName(){return userRow.name;}
// implement setName in an analogous manner
uint32_t getAge(){return userRow.age;}
// setAge ...
void SayHello();
void SetAge(uint32_t age);
};
The advantage is, the code for UserRow
can be created by some generator in a standard manner, just by utilizing the meta data from a database. UserRow
then becomes your data access layer (DAL), User
, however, is a class of your domain layer.
Moerover, you should consider to move LoadFromDatabase
to another place, like into some repository class, to make the class User
persistent-ignorant.
You could say that your User objects actually are row objects, since they already have the same information. Like you have done now, You have only created an unnecessary abstraction layer which doesn't provide anything useful.
User
where you would useUserRow
. Both classes contain exactly the same data, and you don't have to call theSayHello
andSetAge
functions if you don't need to.User
is a domain object andUserRow
a data transfer object, there's no architectural need to remove the redundancy. Sure, it's slightly annoying, but it has the advantage of being more flexible in light of future changes.