0

I wanted to propose a design pattern I've come up with. I haven't seen it used before which is surprising since it fixes a common problem, so I was wondering if I just haven't found it, or whether my solution is undesirable or breaks any major design principles. If anyone could provide me with any links to where it's been described, or provide opinion or critique. If you have none then feel free to use =)

I often use the Active Record Pattern due to it's simplicity, fully aware of the undesirable coupling it creates on the database, and litters domain objects with ugly SQL code. So my proposition is the following:

interface IUser{
    getFirstName();
    getLastName():
    setFirstName();
    setLastName();
}

class User implements IUser{

   id:string;
   firstName:string;
   lastName:string;

   constructor(id:string, firstName:string, lastName:string){
      this.id=id;
      this.setFirstName(firstName);
      this.setLastName(lastName);
   }

   function getFirstName(){
      return this.firstName;
   }

   function getLastName(){
      return this.lastName;
   }

   function setFirstName(firstName:string){
      this.firstName=firstName;
   }

   function setLastName(lastName:string){
      this.lastName=lastName;
   }

}


class UserDB implements IUser{
   user:IUser;

   constructror(user:IUser){
      this.user=user;
   }

   function getFirstName(){
      return this.user.getFirstName();
   }

   function getLastName(){
      return this.user.getLastName();
   }

   function setFirstName(firstName:string){
      this.this.setFirstName(firstName);

      this.update();
   }

   function setLastName(lastName:string){
      this.setLastName(lastName);

      this.update();
   }

   function update(){
      // sql execution script
      // update user set first_name=getFirstName(), last_name=getLastName() where id=this.user
   }
}

An then in code which creates instances, whether it be a factory or repo of User would then do this:

function getUser(id:string):User{
   // select first_name, last_name from user where id=id;

   var user=new User(id, firstName, lastName);
   var userdb=new UserDB(user);

   return user
}

This is perhaps a bad example since typically a User doesn't change their first or last name but it was the simplest example I could come up with!

I am essentially using the GoF Decorator pattern, but have never seen it used this way for data access.

It still has the undesirable effect of performing a db access per property update, just as the active record pattern does, but doesn't it at least remove the coupling from the User class to the database and removes the ugly SQL code. The UserDB implementation could go in the same package as other data access classes. Does the solution unbreak the SRP principle from the Active Record Pattern. I know that Active Record Pattern is considered by some to be an anti-pattern, but it is easy to use on simple applications, so point is just the following, if you ARE going to use it, wouldn't this is a better implementation for it?

Edit to respond to Matia's comments:

Matias, thank you very much for your comments, critique is exactly what I was looking for. However, I don't agree with your conclusions and let me explain why:

Firstly take a look at your sample User class, it is a domain entity but yet has implications of data storage. A domain object shouldn't have a save() method on it. Where as my example (the User class) truly is a domain object. Domain objects shouldn't have dependancies on repositories, especially in a constructor(!) Repositories are concerned with data storage concerns, so you're immediately declaring to the world that the User domain object is concerned about persistence. In addition a User instance shouldn't know about how to persist itself or how to retrieve other User instances - so this is another major violation.

Whilst I know people do reference Repositories from domain objects, this is considered very bad practice, and there are MANY references to this all over the internet, here are a couple links for convenience:

Is it ok for entities to access repositories?

http://thinkbeforecoding.com/post/2013/03/03/Entities-and-Repository-injection-ndash%3B-follow-up

Please do look at those links if you continue to believe that it is ok for the User class to reference the User Repository, or please share with me any link which arrives at the opposite conclusion.

"Single Responsibility Principle. Your active record owns the persistence logic."

You're absolutely right, UserDB owns persistence logic, but that is exactly what Active Record Pattern is supposed to do, and remember I'm comparing my solution with that one.

But what I have done and the whole point of my proposal is to decouple that persistence logic dependancy from the domain layer, thereby removing SQL from it, since UserDB will reside in a data access layer, allowing my User class to truely belong in the domain layer, whereas yourexample User class could not.

"Dependency inversion principle. You create dependencies on implementations rather than on abstractions."

Regarding depending on a concrete class, I assume you're referencing to the fact that I'm extending the UserDB class from the concrete User class? I was aware about the concrete dependancy, but this could be easily eradicated by introducing an IUser interface and having both User and UserDB implement it, but I was trying to keep the code minimal for the purposes of presentiation and convenience. So I do recognise and agree with that point, however it is easily rectifiable.

"As you can check in my sample, I'm not persisting active record's changes whenever a property is set, but it's done when User.Save() is called, which seems to be a better approach."

If performance is a concern, then I would consider not calling it on each property update, but it again would reside in UserDB and not the User class as your sample does, for reasons I think I've already explained.

Community
  • 1
  • 1
paddyc
  • 79
  • 1
  • 10

1 Answers1

0

fully aware of the undesirable coupling it creates on the database, and litters domain objects with ugly SQL code

Actually this statement might be wrong, because the whole active record can inject a repository which solves the issue from scratch.

In C#:

public class User 
{
    public User(IUserRepository repository)
    {
        Repository = repository;
    }

    private IUserRepository Repository { get; }

    public string Name { get; set; }
    public string LastName { get; set; }

    public void Save() 
    {
         Repository.Update(this);
    }
}

As you can check in my sample, I'm not persisting active record's changes whenever a property is set, but it's done when User.Save() is called, which seems to be a better approach.

Do you want to point out which principles would violate your approach? At least I found two:

  • Single Responsibility Principle. Your active record owns the persistence logic.
  • Dependency inversion principle. You create dependencies on implementations rather than on abstractions.
Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
  • @paddyc It's not the way to answer to an answer ;P BTW, Active Record pattern has pros and cons. In my case, I don't like this design pattern and I don't use it. And as of your proposal of using inheritance instead of composition, there's a big design flaw in your reasoning: if `UserDB` inherits `User`, then `UserDB` *is a specialized domain object*. – Matías Fidemraizer Jan 31 '17 at 07:25
  • @paddyc And as of the thing of not injecting repositories in domain objects... IMO, who said that an active record is a domain object...? – Matías Fidemraizer Jan 31 '17 at 07:27
  • @paddyc About the Dependency Inversion Principle, you're executing SQL directly in your `UserDB` class. I expect there some concrete implementations to perform DB operations since I don't see where you inject abstractions in the `UserDB` constructor... – Matías Fidemraizer Jan 31 '17 at 07:29
  • @paddyc IMHO, I believe that you won't get more from a StackOverflow answer, because this isn't a forum where we discuss and evolve a conversation to introduce more background overtime... It's more about asking a very concrete issue and getting a very concrete answer. If you want to go further, find out what questions have arised from reading my answer and ask them separately. Otherwise, we can end up with a design patterns book published as a Q&A :D – Matías Fidemraizer Jan 31 '17 at 07:31
  • Sorry, wasn't sure the correct way to respond with a larger comment then it allows in a comment box but you've just shown me =) – paddyc Jan 31 '17 at 12:27
  • Bear in mind that I am using interface inheritance for introducing the UserDB class (I've made the amendment to the code above as per my last response in case this wasn't clear), and then delegating the method calls to the User class, so I'm not convinced that I'm making a _specialized_ version of the User class, I am essentially applying the Decorator Pattern which GoF defines as: "Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality". – paddyc Jan 31 '17 at 12:28
  • Who said that Active Record Pattern is a domain object? Most definitions that you could find probably. Martin Fowler states: "An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data." Assuming that domain logic only appears in the domain layer then it's not a unreasonable assumption to make right? – paddyc Jan 31 '17 at 12:29
  • Admittedly this is exactly why ARP is considered an anti-pattern, however this domain - data access coupling is exactly what I'm trying to avoid in my proposal whilst mainitaing a clean DDD domain model. Have I not acheived this? Your User class example certainy doesn't. – paddyc Jan 31 '17 at 12:29
  • @matis Yes you're right, the UserDB class would have a db connection instance passed in or similar. However this would be passed in by something on the data access layer, and the UserDB class would also sit in that layer. – paddyc Jan 31 '17 at 12:30
  • You make a good point, perhaps stackoverflow isn't the right place to discuss this, and we may be in danger of being flagged as being opinionated which I'm trying to avoid by backing up all arguments. However I really appreciate your comments, if I am mistaken in my conclusions (which I'm not yet convinced I am), then am here to understand why and learn from them. If you know of another site which better to openly discuss and debate this sort of thing then please let me know, and many thanks =) – paddyc Jan 31 '17 at 12:31
  • @paddyc Maybe your assumption is wrong :D A domain model has also behavior like an active record, but it's not a domain object. Thus, I would conclude that an active record isn't a domain object. – Matías Fidemraizer Jan 31 '17 at 13:23
  • @paddyc Actually I don't know where, at least in StackExchange network. A year ago I opened this site: http://designpattern.ninja, and it's open to contributors. I created DesignPatternNinja to let unknown people like you and me define existing and invented design patterns. I also publish blog posts there and everyone can do it too. The site is made with Jekyll and contributions are sent using regular GIT flow (pull requests). – Matías Fidemraizer Jan 31 '17 at 13:25
  • @paddyc Probably an issue on the [GitHub repository](https://github.com/designpatternninja/designpatternninja.github.io) could be a good site to discuss together your concerns in a limitless space. If you want to open an issue [there](https://github.com/designpatternninja/designpatternninja.github.io), we can continue this discussion and go deeper in the topic! – Matías Fidemraizer Jan 31 '17 at 13:28
  • @matis I think we've found where our fundumental disagreement is, whether or not an Active Record is a domain object. Infact there was a more unambiguous quote from Martin Fowler: https://www.martinfowler.com/eaaCatalog/activeRecord.html _"An object carries both data and behavior. Much of this data is persistent and needs to be stored in a database. Active Record uses the most obvious approach, putting data access logic in the domain object"._ With that I rest my case and maybe wait and see if anyone else weighs in. – paddyc Jan 31 '17 at 23:15
  • @matis btw, I just saw your site, very cool and definitely be checking it out, and perhaps debate anything else which will help me improve my understanding =) – paddyc Jan 31 '17 at 23:16
  • @paddyc oops actually I didn't remember that an active record was defined as a domain object after all.... Well, anyway I prefer a better separation of concerns and I've been always away of this pattern. – Matías Fidemraizer Jan 31 '17 at 23:32
  • @paddyc ah and thank you for your good comments about design pattern.ninja. I hope that some day many people will be ready to contribute with content! And of course, if you want to discuss some topic about software architecture, feel free to open an issue there and let's go for it! – Matías Fidemraizer Jan 31 '17 at 23:35