Firstly regarding having zip codes as strings versus having a ZipCode
type that holds the string. There si a growing school of thought that favours the latter as it offers increased type safety, eg (in C# 7 syntax for brevity, but the language isn't important):
public class ZipCode
{
pubic string Value { get; }
public ZipCode(string value)
{
Value = ValidZipCode(value) ? value : throw new Exception("That's not a zip code!!");
}
...
}
...
string zipCode1 = "elephant" // compiles, but is nonsense
ZipCode zipCode2 = new ZipCode("elephant"); // exception thrown
However, there are definite disadvantages in using a "heavy" type that contains/performs mapping details etc, when all you need is the zip code value. For example, to get a location, this type might need to perform a database lookup and thus either database details need to be injected into every instance, or it has to become coupled to a database service locator, thus making testing harder.
So in that casefor cases where the full details are needed, create a ZipCodeDetails
class too, that contains that extra data. "zip code" will cause most people to picture just a zip code, so don't call your details class that. You hopefully will think of a better name than just ZipCodeDetails
as "details" doesn't convey a great deal of information, but it'll hopefully give you the idea.