How to Refactor a Multi‑Tenant Service to Reduce Coupling and Code Bloat
The article presents a real‑world case of a multi‑tenant system whose business logic was tightly coupled across tenants, leading to a massive service class, and demonstrates how applying Domain‑Driven Design, the Dependency Inversion Principle, and the Factory pattern can restructure the code into isolated tenant‑specific actions, improving maintainability and testability.
Background
The author shares a code‑optimization case of a multi‑tenant project that isolates tenants by a database field and uses a single backend service built on an MVC architecture.
Problems Identified
Business code across tenants is highly coupled, making changes risky.
Testing cost is high because any change requires retesting all tenants.
The service class has grown to over 1,000 lines, containing logic for every tenant, which hampers maintenance and extension.
Redesign Principles
Domain‑Driven Design (DDD): Move business rules to the domain layer, isolating each tenant’s logic in its own domain component.
Dependency Inversion Principle (DIP): Abstract common behavior into interfaces or abstract classes; concrete tenant‑specific details depend on those abstractions.
Factory Pattern: Use a factory to obtain the appropriate tenant‑specific implementation at runtime.
Refactored Code Example
Controller
@PostMapping("/infoByNamePage")
@ApiOperation(value = "分页查询", notes = "分页查询")
public R<Page<OrderInfoPageVO>> infoByNamePage(@Valid @RequestBody OrderInfoVoRequest orderInfoVoRequest) {
OrderAction orderAction = OrderFactory.getOrderAction(CurrentUserUtil.getTenantId());
return orderAction.infoByNamePage(orderInfoVoRequest);
}In this snippet, OrderAction is a top‑level interface that defines all business behaviors for a tenant.
OrderFactory Implementation
public class OrderFactory implements ApplicationContextAware {
// Tenant A
private static final String ORDER_TENANT_A = "A";
// Tenant B
private static final String ORDER_TENANT_B = "B";
@Autowired
private OrderTenantConfig config;
private static Map<String, OrderAction> orderActionMap = new HashMap<>(2);
/**
* Retrieve the OrderAction implementation for the given tenant ID.
*/
public static OrderAction getOrderAction(String tenantId) {
OrderAction orderAction = orderActionMap.get(tenantId);
if (ObjectUtil.isEmpty(orderAction)) {
throw new OrderBusinessException(OrderCodeEnum.TENANT_ID_IS_NULL);
}
return orderAction;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// AOrderAction and BOrderAction are the concrete implementations for each tenant.
orderActionMap.put(config.getMap().get(ORDER_TENANT_A), applicationContext.getBean(AOrderAction.class));
orderActionMap.put(config.getMap().get(ORDER_TENANT_B), applicationContext.getBean(BOrderAction.class));
}
}Result of Refactoring
After the redesign, the original 1,000‑line service is split into three classes: AbstractOrderAction (≈500 lines) – contains shared logic. AOrderAction – tenant‑A specific implementation. BOrderAction – tenant‑B specific implementation.
This separation decouples tenant code, making the system easier to maintain and extend.
Conclusion
The presented design works well for moderate‑size multi‑tenant projects, but it may become unsuitable when business complexity and the number of tenants grow excessively; in such cases, further decomposition or alternative architectural approaches should be considered.
There is no "best" design, only the most suitable one for the given business size.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architect
Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
