My team has been developing a web business application for more than a year. It started quite small, but now it is growing bigger and bigger. I think it’s time for refactoring.
Looking at the code, I see the below situation:
IUserService
- addUser
- deleteUser
- updateUser
- addMember
- deleteMember
- updateMember
- registerMember
- deactivateMember
…. - getExpiringMember
- nominateMember
Generally saying, there are about 50 functions for more than 2500 lines of business logic. I once read “Clean code”, it states that there should be no more than 200 lines for a normal class. Our code violates this requirement seriously, and I’m beginning to feel the hardship of code management.
The simple solution might be spliting this service into many other services, but it is not feasible/reasonable:
-
This service class serves as an API point for mobile client, so that every change on Interface (even renaming) requires change for the client as well. I don’t want to spend the precious time of our team if not for good reason.
-
I already split a part of this service into another classes, but that class (RegistrationService) also grow quite fast and reach 500 lines of code recently.
-
More and more business logic request recently, and these services are going to grow even more.
I’m afraid that at some point we will lose the tracks if a team member who manage a specified service got retired.
Is there any way I can make this service more manageable? Any Design Pattern, Refactoring technique that I can apply? I’m still not experienced enough in this field… any suggestions or directions are welcome.
I’m not a Spring expert but from general design/architecture experience this looks like way too many methods for a single API / Service. It’s something of a design smell to see this many methods in one interface.
You should try an break it down into logically distinct areas of functionality, each with a separate interface. Then the code to implement each distinct area should become manageable.
If you have client code that is hard to change, then it’s fine to keep the old version of the service – just turn it into a lightweight facade that calls into the new refactored versions. Your clients won’t have to change anything if they don’t want to (though you should make clear that they will have to move to the new interfaces if they want access to any future functionality).
From the just the methods listed in the question above you could have at least four separate domains:
- User management
- Member management
- Registrations (registerMember etc.)
- Nominations (nominateMember etc.)
- etc.
1
It seems you can’t split your service interface into different classes / services, to maintain backwards compatibility.
What you should do though, is split the functionality into classes according to SRP, and then (best-case) have interfaces to those classes as dependencies in the constructor of your service class or (worse-case) instatiate them all in the constructor.
Each method in the service class then becomes a single line of wrapping goodness, and the rest of your application can be sanely thought out without changing the public API.
Example:
public class Service : IUserService
{
private IUserRepository userRepo;
private IMemberStuff memStuff;
public Service(IUserRepository userRepo, IMemberStuff memStuff)
{
this.userRepo = userRepo;
this.memStuff = memStuff;
}
public void addUser(...)
{
userRepo.addUser(...);
}
public void deleteUser(...)
{
}
public void updateUser(...)
{
}
public void addMember(...)
{
}
public void deleteMember(...)
{
}
public void updateMember(...)
{
}
public void registerMember(...)
{
}
public void deactivateMember(...)
{
}
public Member getExpiringMember(...)
{
}
public void nominateMember(...)
{
}
}
1