How a 2‑Year Journey Unified Java Backend Code Structure for Better Readability and Maintenance
This article shares a two‑and‑a‑half year effort to standardize code style and architecture across a large Java backend, detailing the origins, evolving patterns, a unified layered model, concrete implementation examples, measurable benefits, and future directions for continuous improvement.
Background and Motivation
The author reflects on a 2.5‑year practice of improving code style and structural consistency within a team responsible for a complex live‑stream commerce commission system, aiming to reduce errors, lower onboarding cost for newcomers, and make business logic easier to understand.
Evolution of the Service Layer
Early MVC patterns (Controller → Service → DAO) gave way to modern entry services that handle RPC, HTTP, messaging, or scheduled tasks, while DAO‑like layers now call other distributed services. The business‑logic layer (formerly Service) lacked any structural guidance, leading to divergent implementations.
Observed Code Patterns
Simple method split into private helper methods for readability.
Parallel execution using thread pools to shorten response time.
Responsibility‑chain pattern that breaks processing into linked nodes.
Parallel chain with dependency handling.
Strategy‑pattern based on business‑type identifiers, which can proliferate without clear constraints.
Proposed Unified Layered Architecture
The team introduced a standardized hierarchy that can be visualized as:
Key layers include:
Entry Service : validates parameters, performs object conversion, and delegates core logic to the business process.
Business Process & Activities : a sequence (or parallel set) of activities that orchestrate domain services; each activity acts as glue code.
Domain & Domain Services : encapsulate the core business capabilities of an entity.
Domain Service Extension Points : generic templates that allow strategy‑pattern extensions for varying business rules.
Implementation Details
Concrete examples illustrate the new structure. An entry service starts a process engine, builds a context, and returns a result:
/**
* This is an HSF interface Provider implementation, typical try‑catch omitted
*/
@ServiceEntrance(name="Attribution Billing Service")
@Override
public Result<RatingDTO> rating(RatingRequest request) {
validateRatingParam(request);
// Build process context and start the workflow
RatingProcessContext context = new RatingProcessContext();
context.setRatingRequest(request);
ProcessResult processResult = processEngine.start(ProcessNS.PROCESS_RATING, context);
if (processResult.isSuccess()) {
return Result.success(buildRatingResultDTO(context));
} else {
return Result.fail(processResult.getErrorCode(), processResult.getErrorMessage());
}
}The corresponding XML process definition shows sequential and parallel activities:
<?xml version="1.0" encoding="UTF-8"?>
<onion-process-config>
<!-- Basic logical orchestration: sequence, parallel, condition, loop, etc. Most used is simple sequence -->
<process id="rating_process" name="Content Attribution Billing Process">
<node-list>
<activity name="Initialize Data" bean="ratingInitActivity"/>
<activity name="Online Attribution" bean="ratingAttributeActivity"/>
<activity name="Post‑Attribution Init" bean="ratingAttributeAfterInitActivity"/>
<activity name="Blacklist Check" bean="ratingBlackCheckActivity"/>
<activity name="Calculate Ratio" bean="ratingRatioActivity"/>
<activity name="Sync Accounting" bean="ratingAutoPayCenterActivity"/>
<activity name="Init External Track" bean="ratingInitOuterTrackRuleActivity"/>
<activity name="Build Commission List" bean="ratingCommissionListActivity"/>
<activity name="Build Extra Commission" bean="ratingExtraCommissionListActivity"/>
<activity name="Assemble Result" bean="ratingResultBuildActivity"/>
</node-list>
</process>
<!-- Parallel example -->
<process id="itemCompleteParallelProcess" name="Item Completion Process" parallel="true" executor="itemCompleteExecutor" timeout="500">
<node-list>
<activity name="Activity A" bean="itemCompleteActivityA"/>
<activity name="Activity B" bean="itemCompleteActivityB"/>
<activity name="Activity C" bean="itemCompleteActivityC"/>
<activity name="Activity D" bean="itemCompleteActivityD"/>
<activity name="Activity E" bean="itemCompleteActivityE"/>
<activity name="Activity F" bean="itemCompleteActivityF">
<dependency-list>
<!-- F depends on A completing first -->
<dependency ref="itemCompleteActivityA"/>
</dependency-list>
</activity>
</node-list>
</process>
</onion-process-config>Domain service examples show how attribution and billing logic are encapsulated and how strategy extensions are invoked:
@Domain(id = DomainNS.DOMAIN_TCP_RATING, name = "Attribution Billing Domain")
public class RatingDomainService implements IDomainService<TcpRatingExtTemplate> {
/** Attribution domain service */
@DomainService(name="Attribution Service")
public AttributeResultDTO attributeTo(AttributeToRequest request) {
List<TcpTrackDTO> trackList = request.getRatingRequest().getTrackList();
TcpTrackDTO lastClick = trackList.get(trackList.size() - 1);
AttributeResultDTO result = new AttributeResultDTO();
result.setBizcode(lastClick.getBizcode());
result.setTrackDTO(lastClick);
return result;
}
/** Billing domain service */
@DomainService(name="Billing Service")
public List<RatingDetailDTO> ratioCalculate(RatioCalculateRequest request) {
List<RatingDetailDTO> resultList = Lists.newLinkedList();
for (RatioCalculateInstance instance : request.getRatingInstance().getRatioCalculateInstanceList()) {
BizTypeRatingInput input = new BizTypeRatingInput();
input.setInstance(instance);
BizTypeRatingOutput output = this.invokeFirstMatched(instance,
TcpRatingExtTemplate.EXT_TCP_BIZTYPE_RATING,
ext -> ext.bizTypeRating(input));
if (output != null) {
resultList.addAll(output.getRatingDetailList());
}
}
return resultList;
}
}Benefits and Results
12 backend services migrated to the new model.
Over 600 entry services, 400+ processes, 180+ domains, and more than 1,000 domain services now follow the same structure.
Code readability improved: developers can locate a process file and instantly understand the workflow.
Consistent patterns reduce learning cost when moving between domains.
Standardization enables visual tooling (IDE plugins) for navigation and architecture inspection.
Future Directions
Planned work includes adding monitoring and alerting at each architectural layer, leveraging the standardized model for AI‑assisted code generation, and further exploring advanced orchestration features such as sub‑processes, groups, and custom composers to balance declarative workflow definitions with hand‑written Java when necessary.
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.
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.
