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 for 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.