This is a noob (I think) Event Sourcing
question.
As an example we have:
a shoemaker Bob
a customer Alice
Shoemaker Bob has:
"shoemaker_business_locations" (business location addresses)
first address is "88d8 - 5 Baker Street" (Added in January)
second address is "73c6 - 6 Cadman Plaza" (Added in March)
at certain point (in October) shoemaker moves from
"5 Baker street" to "11 Baker street" (couple of houses down the block)
Say we have a "shoe_repair_order" (Aggregate Root?)
We have a shoe repair order from Alice that happened
sometime in July, before shoemaker Bob moved
his business location down the block.
Let's say that Alice is looking at this order in December.
Obviously she does not have to know that Bob moved his business.
When she sees her order, I think she should see (at least) the old
business location and/or
"Ordered at 5 Baker Street (New address is 11 Baker street - updated on October 6)".
Question: How do we design the Event-Sourced system to show the old addresses for orders?
Do we have to always store an event for our order, like
"OrderLocationAdded" and store a copy of an actual address with our order event -
that way every order would have a correct old location simply taken from events.
Only by issuing new events such as "OrderLocationUpdated" we can then update it
for that particular order if need be.
OR
Do we issue an "OrderLocationAdded" event with a reference to an address
"88d8 AT VERSION 1", so that we know to look for VERSION 1 of our business location.
We will have to replay events on "shoemaker_business_locations"
until we see "version X" of our address (version 1 in our case).
That is what we will have to do to all of older orders whose version of
addresses are below the version that is set currently (version 1).
Some other way?
Maybe our read-model just stores all of the address information at different versions, and when queried for a certain version we
don't have to replay events in our write-model, but simply grab an address that is at version 1 from our read-model?
I think the best way to do this will ultimately depend on how you've designed your Domain Model, but here's how I would approach the problem:
We need to represent Addresses in our Domain Model. This means, addresses will either be Value Objects, Entities, or Aggregates. Of the three, an Address could really only be a Value Object or an Entity in this Domain, since we are a shoe repair application and not an address book. Entities have certain properties, such as being distinguished by their ID, and certain restrictions, like only being part of one Aggregate, that Addresses don't seem to meet. Therefore, I would see an Address being a Value Object in this Domain.
When defining the OrderLocationAdded event on the "shoe_repair_order" Aggregate, you need to decide whether to include the full details of the Address on the event, or use a reference to the Address. Since the Address is a Value Object (it is defined by its data, not by an ID), the answer would be to include the Value Object on the event. This way, when you replay the events to build the current state of the order, it will still have the details of the original address.
If you want to also display the new address on the order, you can use another event, such as BusinessMovedToNewLocation, with the details of the new address, and add this to the order. This event would not replace the original address, it would just allow you display the informational message that the business is now located at a different location.
The answer to your problem certainly depends on how you have defined your domain models (specifically how your Aggregates are defined).The way I would approach this problem is to have two aggregates:
ShoeMaker Aggregate (which in your case stores all the information like name of the shoemaker business, info about this business' owners, list of all the addresses for this shoemaker where each address can have information like email, phone number, and a bool field called current which can be true for the current address. I imagine this AR would have a command called - "ChangeShoeMakerCurrentAddress" which would raise an event called "ShoeMakerAddressesChanged".
Order Aggregate (which would have all the information about an order like customer name, price, details, order date, delivery date etc.).
Let's say we have a read-model called "ShoeMakerOrderReadModel" which can subscribe to events raised by ShoeMaker Aggregate and Order Aggregate. This read-model listens to events and denormalize them into a POJO/POCO object. This POCO/POJO model can have a list of shoemaker addresses (where each address can have information like email, phone number, and a bool field called current which can be true for the current address.) This AR can definitely subscribe to "ShoeMakerAddressesChanged" events from ShoeMaker Aggregate and then update the list of shoemaker addresses.
I hope this helps!
The answer is very simple thanks to CQRS: you must not listen to BusinessMovedToANewLocationEvent in the OrderDetailsReadModel. You just lookup and store only once the current Business address in the Order details in this Read model when the OrderPlacedEvent happens.
In this way you don't have to include any Address value object into the event. The Read model does all the work.
You could listen to the event, if you need for other reasons, but just don't update the address. Fir example, the Read model could be even smarter and additionally keep an addressChangedInTheMeanTime boolean flag in order to tell Alice that the Address that she sees is an old one but thus depends on your business needs.
Here are a few options:
In your shoemaker read model, include the list of addresses including the dates they became active. This way, when you process an order event in your order projection, you query the shoemaker read model and get the address that's correct for the order date (this will work even if you are rebuilding the projection later). The disadvantage of this are coupling between the two views (order projection now depends on shoemaker's query API, so you need to make sure the shoemaker projection is up to date and available)
If you have a way of subscribing to multiple event streams so you receive events in approximate global order, in your order projection also handle shoemaker address events (creation and updates). Keep a mini table of shoemaker to 'current' address (meaning current as of the events you have processed, allowing replays to work correctly). Then when handling order creation events, just lookup the current address in the table and copy it into the order view. This is a bit more code, but avoids dependencies between views and avoids the need for address history in the shoemaker view (although you might need that anyway for other reasons).
Make the shoemaker address part of your order domain model. It will need to be included in the order creation command (possibly by command enrichment in the controller layer, looking it up in the shoemaker read view). This doesn't seem necessary since the order doesn't have a concept of the shop address that's separate from the shop's concept (this is different from, say, delivery address, which is usually a per-order item in some way).
Related
I want every order to have one shipping address. But if I added shipping_addresss_id in order table, then if the user ordered something that is shipped and delivered, and after months the user changed his/her shipping address into something else, then the orders already processed months before will also change into the new address, leading to falsified results. If he/she deleted the address completely, it also would cause problems.
I thought about this solution:
whenever the user orders I will provide him/her with all his/her addresses from shipping_address table and the user will chose one , but instead of pointing into shipping_address_id , I will get the hard string of address and save it in the order. Is it a good solution?
Here the relevant parts in my current ERD:
This is the whole database
Indeed, there is a difference between the curent list of possible shipping addresses that a customer can chose for new orders, and the historical addresses used for shipping that cannot change once the parcel left the warehouse.
There are three main approaches to address this problem:
Denormalization: have the required address fields in the order and don't change it anymore once the order is in a shipment status (e.g. "ready to be collected", or "picked by carrier"). The easiest way to populate this shipped order address seems to be to copy the valid shipping address at the moment of the status change.
Pros:
easy to implement and use, doesn't affect the management of (desired) shipping addresses.
Cons:
the order would contain information that corresponds to a different concern, which makes maintenance difficult: if address format would change, you would have to change also the order entity.
the order would contain lots of redundant addresses.
Moreover, you may have duplicate code, since displaying the effectively shipped address would be performed differently than desired shipping address.
Add a shipped_address entity for the effective order shipping address: you'd just store there the address used at the moment of the shipping, with a relation to the relevant order.
Pros&Cons: It reduces somewhat the cons of the first option, by isolating the address in a separate table, but is very similar.
Smart shipping address management: rethink the way the shipping address is used. Your order should refer as well to the shipping address. In this case, you would have to make sure that shipping addresses used in shipped orders are not changed anymore (e.g. using a flag). If the address of a customer would change and the current address is such a flagged address, you'd then create a new address instead of the existing one. Since customer can have several desired shipping addresses, you'd then also need to take care of whether a customer still wants to use the historical address or not.
Pros:
denormalization is ensured and redundant addresses are prevented
updates of the shipping address can be done as long as no order was effectively shipped to that address, and in a single place
the shipping address of orders already shipped is preserved
deploying shipping address is always done the same way, whether it's an historical address or a real one.
Cons:
requires a more careful management of the shipping addresses.
Conclusion: I'd advise to go for option 3. It requires you to think a little bit more about shipping addresses and address state, but ultimately would lead to a more powerful solution.
MyOB's AccountRight Documentation page provides a sample for Editing a Customer based on its UID & RowVersion. However, it does not cover how to update specific address records associated to that customer.
Say for example i have a customer with business name "My Business Customer" and has 5 addresses saved on it. How do i update Address #3 while keeping the original records for Addresses 1, 2, 4, and 5?
Adding only the specific updated Address Record on the Customers "Address" JSON Property removes all other address but that.
What you've described is the desire to use the PATCH HTTP verb. Unfortunately, last I checked (and as per the current docs, MYOB APIs still only support PUT which means you have to provide the full, complete JSON object as it essentially 'replaces' what's in the customers company file.
Your API calls and code would follow something similar to the following steps:
GET the /Contact/Customer/{guid}
Make modifications to the data (in your case, update Address 3)
PUT to the /Contact/Customer/{guid} URL with your updated object.
Naturally you might not want to do this every time, so you can GET and cache the result, and use the RowVersion to determine if there's something out of date in your cache. If so, expect a HTTP 409 error because the RowVersion you provide in your PUT doesn't match the latest RowVersion of the resource in the API - but the errors will help guide you there.
I'm looking to attempt to simplify address entry into a system where the city textbox has autosuggest initially populated by the user's geolocation. In the past it has seemed that autosuggesting the city name is prohibitively costly without knowing the province/state/country first but it doesn't make sense to require the user to enter the address backwards as we don't think about address information this way. On the other hand, not autosuggesting the city name means we end up with all sorts of weird and wonderful entries for mis-spelled cities from around the world.
I was wondering if there's a service that I can query that would automatically respond with the most appropriate city names according to not only what the user enters in the textbox, but the location of that user based on the country and political boundary they fall within?
For instance, if I am in Canada [as I am] and I enter 'Mi' then I'd be presented with all cities within Canada starting with 'Mi' until it was determined that the information I was entering wasn't Canadian at which point, it would use the next most likely configured country based on our usage pattern - i.e. it would check the U.S. next, followed by Mexico and then other less likely destinations. I can write all this myself if I had the database but I don't know where I can find one and my suspicion is that it would be less scalable than querying a pre-existing service on the web.
Looks as though MaxMind offers a free database that you could download in CSV:
There's an online demo to test it a bit if you'd like, but no way to query it through a web service.
IPInfoDB also has their database available for download - they have an XML API, but it only supports looking up the city/country for a particular IP. You're trying to do something a little more wide than that, looking for every city in a particular country, with country selected based on IP. I wouldn't expect that there's a web service for that, it's a pretty specific requirement.
Edited to add: You could use the IPInfoDB API to look up the country though, and then generate the autocomplete suggestions from a local country/city database. That way all the IP-geolocation wouldn't need to be done locally. There are various places that you can get a list of cities in a particular country. For example, here's some comprehensive lists maintained by the National Geospatial-Intelligence Agency
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
Are there any best practices (or even standards) to store addresses in a consistent and comprehensive way in a database ?
To be more specific, I believe at this stage that there are two cases for address storage :
you just need to associate an address to a person, a building or any item (the most common case). Then a flat table with text columns (address1, address2, zip, city) is probably enough. This is not the case I'm interested in.
you want to run statistics on your addresses : how many items in a specific street, or city or... Then you want to avoid misspellings of any sorts, and ensure consistency. My question is about best practices in this specific case : what are the best ways to model a consistent address database ?
A country specific design/solution would be an excellent start.
ANSWER : There does not seem to exist a perfect answer to this question yet, but :
xAL, as suggested by Hank, is the closest thing to a global standard that popped up. It seems to be quite an overkill though, and I am not sure many people would want to implement it in their database...
To start one's own design (for a specific country), Dave's link to the Universal Postal Union (UPU) site is a very good starting point.
As for France, there is a norm (non official, but de facto standard) for addresses, which bears the lovely name of AFNOR XP Z10-011 (french only), and has to be paid for. The UPU description for France is based on this norm.
I happened to find the equivalent norm for Sweden : SS 613401.
At European level, some effort has been made, resulting in the norm EN 14142-1. It is obtainable via CEN national members.
I've been thinking about this myself as well. Here are my loose thoughts so far, and I'm wondering what other people think.
xAL (and its sister that includes personal names, XNAL) is used by both Google and Yahoo's geocoding services, giving it some weight. But since the same address can be described in xAL in many different ways--some more specific than others--then I don't see how xAL itself is an acceptable format for data storage. Some of its field names could be used, however, but in reality the only basic format that can be used among the 16 countries that my company ships to is the following:
enum address-fields
{
name,
company-name,
street-lines[], // up to 4 free-type street lines
county/sublocality,
city/town/district,
state/province/region/territory,
postal-code,
country
}
That's easy enough to map into a single database table, just allowing for NULLs on most of the columns. And it seems that this is how Amazon and a lot of organizations actually store address data. So the question that remains is how should I model this in an object model that is easily used by programmers and by any GUI code. Do we have a base Address type with subclasses for each type of address, such as AmericanAddress, CanadianAddress, GermanAddress, and so forth? Each of these address types would know how to format themselves and optionally would know a little bit about the validation of the fields.
They could also return some type of metadata about each of the fields, such as the following pseudocode data structure:
structure address-field-metadata
{
field-number, // corresponds to the enumeration above
field-index, // the order in which the field is usually displayed
field-name, // a "localized" name; US == "State", CA == "Province", etc
is-applicable, // whether or not the field is even looked at / valid
is-required, // whether or not the field is required
validation-regex, // an optional regex to apply against the field
allowed-values[] // an optional array of specific values the field can be set to
}
In fact, instead of having individual address objects for each country, we could take the slightly less object-oriented approach of having an Address object that eschews .NET properties and uses an AddressStrategy to determine formatting and validation rules:
object address
{
set-field(field-number, field-value),
address-strategy
}
object address-strategy
{
validate-field(field-number, field-value),
cleanse-address(address),
format-address(address, formatting-options)
}
When setting a field, that Address object would invoke the appropriate method on its internal AddressStrategy object.
The reason for using a SetField() method approach rather than properties with getters and setters is so that it is easier for code to actually set these fields in a generic way without resorting to reflection or switch statements.
You can imagine the process going something like this:
GUI code calls a factory method or some such to create an address based on a country. (The country dropdown, then, is the first thing that the customer selects, or has a good guess pre-selected for them based on culture info or IP address.)
GUI calls address.GetMetadata() or a similar method and receives a list of the AddressFieldMetadata structures as described above. It can use this metadata to determine what fields to display (ignoring those with is-applicable set to false), what to label those fields (using the field-name member), display those fields in a particular order, and perform cursory, presentation-level validation on that data (using the is-required, validation-regex, and allowed-values members).
GUI calls the address.SetField() method using the field-number (which corresponds to the enumeration above) and its given values. The Address object or its strategy can then perform some advanced address validation on those fields, invoke address cleaners, etc.
There could be slight variations on the above if we want to make the Address object itself behave like an immutable object once it is created. (Which I will probably try to do, since the Address object is really more like a data structure, and probably will never have any true behavior associated with itself.)
Does any of this make sense? Am I straying too far off of the OOP path? To me, this represents a pretty sensible compromise between being so abstract that implementation is nigh-impossible (xAL) versus being strictly US-biased.
Update 2 years later: I eventually ended up with a system similar to this and wrote about it at my defunct blog.
I feel like this solution is the right balance between legacy data and relational data storage, at least for the e-commerce world.
I'd use an Address table, as you've suggested, and I'd base it on the data tracked by xAL.
In the UK there is a product called PAF from Royal Mail
This gives you a unique key per address - there are hoops to jump through, though.
I basically see 2 choices if you want consistency:
Data cleansing
Basic data table look ups
Ad 1. I work with the SAS System, and SAS Institute offers a tool for data cleansing - this basically performs some checks and validations on your data, and suggests that "Abram Lincoln Road" and "Abraham Lincoln Road" be merged into the same street. I also think it draws on national data bases containing city-postal code matches and so on.
Ad 2. You build up a multiple choice list (ie basic data), and people adding new entries pick from existing entries in your basic data. In your fact table, you store keys to street names instead of the street names themselves. If you detect a spelling error, you just correct it in your basic data, and all instances are corrected with it, through the key relation.
Note that these options don't rule out each other, you can use both approaches at the same time.
In the US, I'd suggest choosing a National Change of Address vendor and model the DB after what they return.
The authorities on how addresses are constructed are generally the postal services, so for a start I would examine the data elements used by the postal services for the major markets you operate in.
See the website of the Universal Postal Union for very specific and detailed information on international postal address formats:http://www.upu.int/post_code/en/postal_addressing_systems_member_countries.shtml
"xAl is the closest thing to a global standard that popped up. It seems to be quite an overkill though, and I am not sure many people would want to implement it in their database..."
This is not a relevant argument. Implementing addresses is not a trivial task if the system needs to be "comprehensive and consistent" (i.e. worldwide). Implementing such a standard is indeed time consuming, but to meet the specified requirement nevertheless mandatory.
normalize your database schema and you'll have the perfect structure for correct consistency. and this is why:
http://weblogs.sqlteam.com/mladenp/archive/2008/09/17/Normalization-for-databases-is-like-Dependency-Injection-for-code.aspx
I asked something quite similar earlier: Dynamic contact information data/design pattern: Is this in any way feasible?.
The short answer: Storing adderres or any kind of contact information in a database is complex. The Extendible Address Language (xAL) link above has some interesting information that is the closest to a standard/best practice that I've come accross...
What is the "best" way to store international addresses in a database? Answer in the form of a schema and an explanation of the reasons why you chose to normalize (or not) the way you did. Also explain why you chose the type and length of each field.
Note: You decide what fields you think are necessary.
Plain freeform text.
Validating all the world's post/zip codes is too hard; a fixed list of countries is too politically sensitive; mandatory state/region/other administrative subdivision is just plain inappropriate (all too often I'm asked which county I live in--when I don't, because Greater London is not a county at all).
More to the point, it's simply unnecessary. Your application is highly unlikely to be modelling addresses in any serious way. If you want a postal address, ask for the postal address. Most people aren't so stupid as to put in something other than a postal address, and if they do, they can kiss their newly purchased item bye-bye.
The exception to this is if you're doing something that's naturally constrained to one country anyway. In this situation, you should ask for, say, the { postcode, house number } pair, which is enough to identify a postal address. I imagine you could achieve similar things with the extended zip code in the US.
In the past I've modeled forms that needed to be international after the ups/fedex shipping address forms on their websites (I figured if they don't know how to handle an international order we are all hosed). The fields they use can be used as reference for setting up your schema.
In general, you need to understand why you want an address. Is it for shipping/mailing? Then there is really only one requirement, have the country separate. The other lines are freeform, to be filled in by the user. The reason for this is the common forwarding strategy for mail : any incoming mail for a foreign country is forwarded without looking at the other address lines. Hence, the detailed information is parsed only by the mail sorter located in the country itself. Like the receiver, they'll be familiar with national conventions.
(UPS may bunch together some small European countries, e.. all the Low Countries are probably served from Belgium - the idea still holds.)
I think adding country/city and address text will be fine. country and city should be separate for reporting. Managers always ask for these kind of reports which you do not expect and I dont prefer running a LIKE query through a large database.
Not to give Facebook undue respect. However, the overall structure of the database seems to be overlooked in many web applications launching every day. Obviously I don't think there is a perfect solution that covers all the potential variables with address structure without some hard work. That said, combined with autocomplete Facebook manages to take location input data and eliminate a majority of their redundant entries. They do this by organizing their database well enough to provide autocomplete information in a low cost, low error way to the client in real time allowing them to more or less choose the correct location from an existing list.
I think the best solution is to access a third party database which contains your desired geographic scope and use it to initially seed your user location information. This will allow you to avoid doing the groudwork of creating your own. With any luck you can reduce the load on your server by allowing your new users to receive the correct autocomplete information directly off your third party supplier. Eventually you will be able to fill most autocomplete for location information such as city, country, etc. from information contained in your own database from user input data.
You need to provide a bit more details about how you are planning to use the data. For example, fields like City, State, Country can either be text in the single table, or be codes which are linked to a separate table with a Foreign Key.
Simplest would be
Address_Line_01 (Required, Non blank)
Address_Line_02
Address_Line_03
Landmark
City (Required)
Pin (Required)
Province_District
State (Required)
Country (Required)
All the above can be Text/Unicode with appropriate field lengths.
Phone Numbers as applicable.