I've been reading a lot about object oriented programming (OOP) and domain driven design (DDD) over the past few months. I've also been trying to wrap my head around how I can implement OOP coding styles into my daily CFML application development. Most of the time you'll see OOP/DDD applied towards solving rather complex problems, which makes sense in terms of using it for production purposes. However, when trying to learn a concept sometimes it makes sense to take something simple and make it a little more complicated in order to learn. In other words, I need to learn how to walk before I can run. What I've done is set up a simple model for a user registration system that will be used on a web site. I'm sure we've all built tons of these things - a user visits a web site where they are asked to enter their name, email address, password, etc... My goal was to take what I've learned about OOP so far and apply it to a scenario like this. In my reading about OOP I came across a concept called an "anemic domain model" which some people consider to be an anti-pattern. An anemic domain model, as I understand it, is basically using your entity objects (the "user" object in this case) as dumb "bags of getters and setters". Essentially this means your objects don't contain any business logic and aren't all that different different than a standard CFML struct. Many people argue that entities should contain behavior. The difficult part for me is understanding which behavior belongs in an entity, and which belongs in a service. If you add too much behavior in an entity, you could wind up with a massive object that is trying to do too many things at once (a "no-no" if you're following SOLID principals). Walking that thin-line has been a challenge for me. Another thing I came across (special thanks to Hal Helms and Sean Corfield) that really struck a cord with me was that an entity object should never be allowed to exist in an invalid state. I really liked that idea and felt that if I could ensure my entities were never invalid, I could write better code with fewer potential for bugs. My takeaway from this concept is that from the time you create an entity object to the time it evaporates at the end of a request, it should never ever exist in an invalid state. For example, if you had a business rule stating that all users must have an email address. You would need to design your User entity so that emailAddress could never be anything other than a valid email address. So how does all this apply when you're talking about a user registration form? Here's a diagram I made of my model:https://i.imgur.com/NoxX0Eh.png Components: UserService The user service is a singleton object which assists with the getting and saving user data. An important thing to note about a singleton object like this is that it does not manage state! What this means is that this service should be accessible and used by other people using the application. Typically I like to store singletons in the application scope or in a factory which lives in the application scope. UserDAO The user DAO (data access object) is what actually communicates with the database. This object is responsible for running the SQL required for getting, adding, saving, records from the database. This object is injected into the UserService as a dependency. By injecting the object and making it private ensures that only the UserService can make calls to it. User The user object represents an individual user record in the database. This object is also known as an "entity" and contains a specific instance of a user's data. For example, first name, last name, etc. Note: In an attempt to move away from an anemic domain model, you can place business logic in your entity objects. For example, if you wanted to have a requirement for password strength or some other business logic you could include it here. You might also have some logic insuring that the emailAddress field contains a valid looking email address. The "business logic" I've included ensures that the user entity object can never exist in an invalid state. Some people call entities like this "rich domain models" or "rich entities". RegistrationForm The registration form object is also an entity. This object will contain the data for the actual form fields (input boxes, etc...) as well as validation logic to ensure that the data is valid and safe to be inserted into the User entity object. Think of the registration form object as a "proxy" that lives in between the user's input and the actual object they are trying to add or update. You might be asking yourself right about now, "Why do we even need a separate object to hold the form data? Couldn't we just add the data directly to the user entity object?" The reason we use the RegistrationForm object is because a registration form can exist with no data, partial data, or even drastically invalid data. Think about it... when you first visit a registration form all of the text boxes are empty. Is the form in an invalid state? No. You can type anything you want into the name fields, or you could leave them blank. You could type gibberish into the email address field, or make a password with only 1 character. The form, still exists without throwing an exception error. In other words, a form is simply something that accepts user input. If you tried to add a gibberish email address into the User entity object the User entity object would exist in an invalid state. That would be very bad. To prevent invalid states from occurring gracefully, the RegistrationForm entity has some business logic thrown into it, the validate() method. The validate() method checks to see if the data contained within itself is suitable to be placed within a User entity object. If the validation passes, the User entity is updated. If validation doesn't pass, the form shows itself to the user again so they can correct their inputs. Here's a diagram showing how that process works: So there's my attempt at taking something as simple as a user registration form and applying my understanding of OOP principals. I'd love to hear your thoughts on my approach, how you'd handle things differently, and if you have any suggestions for me moving forward. One thing that I'm still wrestling with is the idea of the "anemic domain model" and how much (and what kind) of behavior to include in my entities. I can think of some examples where an object might have a bunch of behavior or business logic and it would make sense to break that out into several different services otherwise your entity object code would be massive and unmanageable. Thanks for reading and I look forward to hearing your thoughts and criticisms.
... View more