.NET单体系统拆分微服务:地址表复用与数据冗余困境
Hey there, let’s work through this together—this is one of the most common hurdles when splitting a monolith into microservices, so you’re definitely not alone in this confusion.
First, let’s tackle your big question: Is this scenario a good fit for microservices?
Absolutely. The problem you’re facing isn’t a flaw in microservices itself—it’s a matter of clarifying data ownership, defining clear boundaries, and setting up smart data sync patterns. Let’s break down solutions to your address reuse pain points:
1. Separate "Master Address Data" from "Business Context References"
The key here is to stop thinking of address as a single entity that needs to live everywhere, and instead split it into two parts:
- Authoritative Master Data: Host all core address records (plus State/Country lookup tables) in a dedicated
Location Service. This service owns the entire lifecycle of addresses—creation, validation, updates, and deletion. It’s the single source of truth for all address data. - Business Context References: Each business microservice (Product A/B/C) doesn’t need a full copy of the address table. Instead, store only:
- A unique
AddressIdlinking back to the Location Service’s master record - A small set of cached display fields (like City, ZipCode, StateName) that you need for search results or quick views.
- A unique
2. Solve the Redundancy vs. Search Tradeoff
Duplicating full address tables across every service is a bad idea—you’ll end up with massive redundancy and impossible consistency issues. But relying on cross-service joins for search will kill performance. The middle ground is event-driven cache synchronization:
- When your business service creates an entity (Incident, Fire Hydrant, Inspection), call the Location Service’s API to fetch the address’s key display fields, then store those alongside the
AddressIdin your local table. - When an address is updated in the Location Service, it publishes an event (e.g.,
AddressUpdatedEvent) to a message broker (like RabbitMQ or Azure Service Bus). All business services that reference that address subscribe to this event and update their cached display fields automatically. - This gives you the best of both worlds: no full data redundancy, and fast local queries for search results. You get eventual consistency, which is acceptable for most address-related use cases.
3. Reuse Code Without Duplicating Domain Logic
You don’t need to rewrite address-related domain classes in every service. Instead:
- Create a shared contracts class library (e.g.,
Location.Contracts) that defines:- DTOs for address data (e.g.,
AddressSummaryDtofor display fields,FullAddressDtofor detailed views) - Event schemas (e.g.,
AddressUpdatedEvent)
- DTOs for address data (e.g.,
- All microservices reference this library to handle address interactions. Each business service only needs a lightweight local class for its address reference (e.g.,
IncidentAddressReferencewithAddressIdand cached fields)—no need to replicate the full address domain logic.
4. Handle Full Address Lookups When Needed
For scenarios where you need the complete address details (like viewing an Incident’s full location), just call the Location Service’s API using the AddressId. This is a low-frequency operation (usually for detail pages) so performance won’t be an issue, and you’re always getting the latest authoritative data.
Example Walkthrough for Your Services
Let’s map this to your specific use cases:
- Location Service: Manages
Address,State,Countrytables. Exposes APIs to create/get/update addresses, and publishesAddressUpdatedEventon changes. - Microservice-1 (Product A):
Incidenttable has columns:IncidentId,AddressId,City,StateName,ZipCode, [other incident fields]. On Incident creation, fetches address summary from Location Service to populate the cached fields. Subscribes toAddressUpdatedEventto update cached fields if the linked address changes. Search queries use localCity/ZipCodefields directly. - Microservice-2 (Product B):
FireHydranttable follows the same pattern—storesAddressIdplus cached display fields, syncs via events. - Microservice-3 (Product C):
Inspectiontable (multi-to-one with Address) usesAddressIdand cached fields for each inspection record, with event-driven sync to keep display data up to date.
At the end of the day, microservices work here because you’re aligning each service with a clear responsibility: Location Service owns address data, business services own their specific domain logic. The sync pattern solves your search and redundancy concerns without breaking microservice principles.
内容的提问来源于stack exchange,提问作者Peter Albanese

