0

I'm working with a fairly complex web application. It's split up into the following layers:

  • Presentation - HTML
  • Service layer - A REST and SOAP API communicating with the business layer
  • Business layer - Contains the business logic.
  • Data access - Provides access to the storage (SQL etc)

The business layers contains classes encapsulating specific areas, such as customer registration, user management and more. The problem we are seeing is that the business layer is starting to get a bit messy. We have a single class handling customer management but as this area of the application grows more and more complex, the class grows and grows and become messy.

For example, we may have the following classes

  • class CustomerManager

    • void CreateCustomer(...)
    • void DeleteCustomer(...)
  • class UserManager

    • void CreateUser(..)
    • void DeleteUser(...)
    • void ActivateUser(...)
    • void InactivateUser(...)
    • void ResetPassword(...)

Creating a customer involves creating users as well. So CustomerManager calls misc methods in the UserManager class. As the application has evolved, creating a new customer means roughly 10 different things needs to be done except for registring the customer in the database, such as informing sales, audit logging, configuring default user accounts, creating a default configuration for the customer, notifying end-users of their auto-generated passwords and more. So CustomerManager.CreateCustomer grows to ~100 lines of fairly hairy code.

I'm trying to think of a good way to handle this but am assuming that there's some common good way to do this which I'm simply not aware of.

I've considered creating "Task"/"Command" classes implementing small sub-processes and then let the CustomerCreation.CreateCustomer simply execute a set of tasks. I would have more classes but they would each do less things.

I've also considering implementing some kind of global application-level event/plug-in systems where CustomerManager.CreateCustomer just creates the customer in the database and then publishes an event that the customer is created. Plug-ins/something can then subscribe to these events and do stuff such as informing sales and logging the fact. Using this method, I wouldn't have to actually update CustomerManager.CreateCustomer when I want to do more stuff which is something which feels attractive to me.

What obvious design pattern am I missing?

3
  • 1
    by implementing this plug-in system are you going to be able to handle all the edge cases, error checking, complex conditionals any better than you would with the language you are using now? so, another way, it seems your problem is you don't want to have to review the whole mess of CreateCustomer when you add some kind of new requirement. Is using plugins going to get rid of the if, ands, and buts, or more likely just hide them away and wait for problems Commented Sep 14, 2013 at 14:22
  • Thanks but I think you are missing my point a bit. Creating a customer in our software is a complex process. I want this process to be as easily understandable as possible. It's not a matter of hiding away code, it's a matter of placing code and logic in the most clear manner as possible, making it easily extendable and testable. I was thinking that maybe separating sub-process into their own components could make them easier to test and understand on their own. I agree that a plug-in/observer-based system may not accomplish this which is why I'm here asking the question in the first place.
    – Nitra
    Commented Sep 14, 2013 at 15:33
  • yeH, you can try to refactor pieces out. but be careful that you are not introducing more complexity than you are reducing. only refactor into a knwn design pattern when the match between what you have and the pattern is crystal clear and, obvious. even then , you may never end up making a second strategy, for example, to complement the one you have, and so all indirection, templating, dependecy injection, abstraction, etc is just confusing. Commented Sep 14, 2013 at 18:10

3 Answers 3

2

As @DocBrown said, plugins are mostly useful when you want to allow third parties to be able to extend your application. But that doesn't mean you can't use similar techniques in your design as what gets commonly used when interfacing with plugins.

For example, if a lot of the code you have is along the lines of "after creating the customer in the databse, components X, Y and Z need to be informed so they can take their appropriate actions", then you can use the Observer pattern there to decouple CustomerRegistration from X, Y and Z (and at a later time, A and B could get added to that list as well).

On the other hand, if your logic has a lot of ifs, buts and unless tests in it, then there is no real way to reduce the complexity of the code, because it is inherent in the business rules that the code represents.

1

Plugins are nice if you want to let third-parties extend your current application, but if that is not the case, you risk to make the architecture more complex than necessary.

Do yourself a favor and before trying to throw randomly some patterns at your business layer, start with small and simple things first:

  • if you have a method with 100 lines of code, start refactoring to many smaller methods.
  • make sure your smaller methods have all a single responsibility (see also: SOLID), and try to achieve a state where the parts of the calling methods work all on the same level of abstraction
  • when you have enough small methods dealing around a specific topic, you may extract them to classes (especially when the smaller methods exclusively share some data)
  • you can have the "command" pattern in mind for the new classes you create. If it is really a good idea to use that pattern will reveal after you have created some of that helper classes and they look all like similar commands, with a command-like interface
3
  • We already try to fairly aggresivly refactor into smaller methods, but if creating a customer consists of 10 sub-processes and each of this sub-processes are depending on configuration, it tends to get a bit complex even after refactoring.
    – Nitra
    Commented Sep 14, 2013 at 15:05
  • Regarding SOLID. Say I have a CreateCustomer method which launches 10 sub-process. I may add new stuff to this method as I get new sub-processes, I may get rid of stuff in it and I may change order of the sub-process. Wouldn't this go against the Open/closed principle since I have to modify the function if I want to add new sub-process? Using something like plug-ins/observer pattern would make it possible to extend complex processes without changing any existing code but merely add more plug-ins/subscribers/observers. I see that this may add complexity, but I'm not convinced in any way.
    – Nitra
    Commented Sep 14, 2013 at 15:38
  • @Nitra: actually, I find this hard to answer without seeing the real code. But if "sub-processes are depending on configuration" - sounds like you need a run-time mechanism for changing the order of execution, which often can be solved by command pattern. I also like Roman Susi's answer, but alas, I cannot tell you here by some statements on PSE how you can apply that to your system, that would need a full-day consultation.
    – Doc Brown
    Commented Sep 14, 2013 at 19:25
1

Component architecture can provide good tools for fighting complexity of the business layer. For example, notifications can be done with registering subscribers (event handlers). A lot of logic can be done by adaptors, which will provide certain specific interfaces to your User and Customer interfaces. This way logic can be decoupled, making large monolithic classes unnecessary. Hopefully your platform already has some proven component architecture you can use, which is much better than implementing "plugins" from scratch.

Older answer (see first comment):

My first impression of having CustomerRegistration and UserRegistration classes with the methods mentioned is that OO analysis has not been properly accomplished. I always thought that having sensible entities at business layer is the only way to prevent the mess. My advice would be to redesign (if it is not late yet) the business layer classes to better reflect business entities and not frontend processes. It may well be, that after such a redesign complexity will be a fraction of what you have now, and, more important, natural representation of the problem domain will answer your goals better than classes, combined with seemingly related functions.

For example, even as such, UserRegistration should not have DeleteUser method! The task naturally belong to UsersManager class or maybe UserRegistry, if you wish.

In short, I think the problem is that classes are formed around action-like entities, which requires much more interconnections (=mess) than class system around (say) User, Customer, and Managers of those two (managers are more like containers).

There is little point of making User Management OOA from scratch. Good examples can be found from many frameworks, and sometimes they handle much more complex things, like fine-grained authorization, role/permission management, group management, etc.

2
  • Sorry, I've updated my post. Whether I call it CustomerRegistration or CustomerManager is not the main issue. Our Customer*/User* classes have a weird suffix which I didn't want to confuse you with, so I replaced that with the "Registration"-suffix. But in reality, our class names are closer to "*Manager" than "*Registration". I agree that having a Delete() operation in a *Registration class could be confusing.
    – Nitra
    Commented Sep 14, 2013 at 15:29
  • Ok. The gist of my suggestion now is to use some component architecture implementation, which will allow to make better code decomposition.
    – Roman Susi
    Commented Sep 14, 2013 at 15:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.