Cloud Native 14 min read

Key Considerations for Microservice Migration: Dependency Management, Interface Versioning, Isolation, and Data Consistency

The article outlines essential practices for migrating to microservices, covering service dependency rules to avoid circular dependencies, interface version compatibility strategies, isolation techniques for data, deployment and business logic, and methods to ensure eventual data consistency using scheduled retries and transactional messaging.

DevOps
DevOps
DevOps
Key Considerations for Microservice Migration: Dependency Management, Interface Versioning, Isolation, and Data Consistency

When splitting a monolithic system into microservices, careful preparation can prevent many pitfalls. The article first discusses service dependency rules that avoid circular dependencies: upper‑level services may call lower‑level services, services at the same level must not depend on each other, lower‑level services cannot call upper‑level services, and all calls must be unidirectional.

Violating these rules leads to problems such as infinite recursive calls and deployment ordering issues. Example code demonstrates a circular call between Order and Pay services:

Order {
   void B(){
     Pay.A();
   }
}
Pay{
   void A(){
     Order.B();
   }
}

Next, the article addresses interface version compatibility. It recommends adding new overloaded methods instead of modifying existing ones and using Dubbo versioning to run old and new providers side‑by‑side. Example Dubbo configurations are provided:

<dubbo:service interface="com.foo.BarService" version="1.0.0" />
<dubbo:service interface="com.foo.BarService" version="2.0.0" />
<dubbo:reference id="barService" interface="com.foo.BarService" version="1.0.0" />
<dubbo:reference id="barService" interface="com.foo.BarService" version="2.0.0" />

The migration steps are: upgrade half of the providers during low‑traffic periods, then upgrade all consumers, and finally upgrade the remaining providers.

The third section focuses on isolation. Data isolation requires each service to own its database, preventing security risks, connection exhaustion, cross‑service table joins, and schema‑change side effects. Deployment isolation suggests grouping services that share high‑load or batch workloads into separate deployment groups. Business isolation separates time‑critical activities (e.g., flash‑sale) from regular traffic, often by pre‑warming data in caches such as Redis.

Finally, the article tackles data consistency. It distinguishes strong consistency (rarely used) from eventual consistency and presents two main solutions:

Scheduled retry tasks that scan failed records and retry idempotently.

Asynchronous message queues with transactional messages (e.g., RocketMQ). The transactional flow includes sending a half‑message, executing a local transaction, sending a commit/rollback confirmation, and handling possible confirmation loss via MQ‑initiated callbacks.

Sample code for a RocketMQ transaction listener and TCC‑style service methods is shown:

//TransactionListener implementation
public class TransactionListenerImpl implements TransactionListener {
    // execute local transaction
    @Override
    public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
        // record orderID and status in shared map
        return LocalTransactionState.COMMIT_MESSAGE;
    }
    // check transaction status
    @Override
    public LocalTransactionState checkLocalTransaction(MessageExt msg) {
        String status = // retrieve from map
        if ("commit".equals(status)) return LocalTransactionState.COMMIT_MESSAGE;
        else if ("rollback".equals(status)) return LocalTransactionState.ROLLBACK_MESSAGE;
        else return LocalTransactionState.UNKNOW;
    }
}
// Order service example
public class OrderService {
    @Hmily(confirmMethod = "confirmOrderStatus", cancelMethod = "cancelOrderStatus")
    public void makePayment() {
        // update order, lock stock, update coupon, send half‑message
    }
    public void confirmOrderStatus() { /* set status to paid */ }
    public void cancelOrderStatus() { /* revert to pending */ }
}

By using these techniques—clear dependency rules, versioned APIs, proper isolation, and reliable eventual‑consistency mechanisms—teams can reduce the risk of service failures and maintain a stable microservice ecosystem.

cloud nativemicroservicesData Isolationservice dependencyTransactional Messagingeventual-consistencyinterface versioning
DevOps
Written by

DevOps

Share premium content and events on trends, applications, and practices in development efficiency, AI and related technologies. The IDCF International DevOps Coach Federation trains end‑to‑end development‑efficiency talent, linking high‑performance organizations and individuals to achieve excellence.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.