Should be localization part of domain in DDD


While following DDD concept I'm struggling on decision if I should make my domain localization aware? I came with two two solutions how to solve this. Both makes domain localization aware in different places. Should I even place localized text to domain? Share your solution for this problem or pros and cons of my two examples. Thanks.


<strong>Example 1</strong>

</blockquote> class Persion { String name; // other fields ommited void rename(String newName) { String oldName = this.name; this.name = newName // publish event with old name and new name } String name() { return name; } } class PersionRepository { void store(Persion persion) { Locale loc = LocaleContextHolder.get().getLocale(); // store object to DAO - create/update fields for context locale } // other methods ommited } <blockquote>

<strong>Example 2</strong>

</blockquote> class Persion { Map<Locale, String> name; // other fields ommited void rename(String newName) { Locale locale = LocaleContextHolder.get().getLocale(); String oldName = this.name.put(locale, newName); // publish event with old name and new name } String name() { Locale locale = LocaleContextHolder.get().getLocale(); return this.name.get(locale); } } class PersionRepository { void store(Persion persion) { // store object to DAO - create/update fields for all locales } // other methods ommited }


In most of cases, the best option is to remove localization from the domain.

Domain classes should only contain data that are relevant to their invariants, since they are responsible for business rules. To retrieve localized descriptions, use projective DTOs and applicative services.

You could use something like this:

public final class VatCode { private final String _code; public VatCode(String code) { // VAT code validation here... _code = code; } @Override public String toString() { return _code; } @Override public boolean equals(Object obj) { // ... } @Override public int hashCode() { // ... } } public class Person { private final VatCode _identifier; public Person(VatCode identifier) { _identifier = identifier; } // some command and some query here... } public class PersonDTO { private final String _vatCode; private final String _personalDescription; public PersonDTO(String _vatCode, String _personalDescription) { this._vatCode = _vatCode; this._personalDescription = _personalDescription; } // other fields here... public String getVatCode() { return _vatCode; } public String getPersonalDescription() { return _personalDescription; } // some more getter here } public interface LocalizedPersonalInformationService { PersonDTO getInformationOf(VatCode person, Locale localization) throws ProperExceptionList; }

That is:

<ul><li>something like a VatCode valueobject (that overrides equals, hashCode and toString) to identify the Person entity</li> <li>a Person entity, holding the minimum amount of data required to ensure <strong>business invariants</strong> and exposing a set of <a href="http://epic.tesio.it/doc/manual/command_query_separation.html" rel="nofollow">command and queries</a></li> <li>a PersonDTO that carries useful descriptions (some call this a <a href="http://gorodinski.com/blog/2012/04/25/read-models-as-a-tactical-pattern-in-domain-driven-design-ddd/" rel="nofollow">read-model</a>)</li> <li>a LocalizedPersonalInformationService that is able to provide PersonDTOs.</li> <li>and (obviously) all the <a href="http://epic.tesio.it/2012/12/05/exceptions-are-the-norm.html" rel="nofollow">needed</a> <a href="http://epic.tesio.it/2013/03/04/exceptions-are-terms-ot-the-ubiquitous-language.html" rel="nofollow">exceptions</a>... :-)</li> </ul>


If at all possible put all your localization in the UI layer. Sometimes people find that difficult to do. For example, I worked on a project where the business logic would throw an exception and that exception would get displayed in the UI. To localize the exception we had to do something like the following (details omitted for brevity, also we had to have a LocalizedRuntimeException and a LocalizedException):

//====ArbitraryBusinessLogic.java==== if(badThing) { throw new SubclassOfLocalizedException(LocalizedStrings.ERROR_FOO,param1,param2); } //====LocalizedException.java==== public class LocalizedException extends Exception { private LocalizationKey localizationKey; Object [] params; public LocalizedException(LocalizationKey localizationKey, Object ... params) { super(); localizationKey = localizationKey params = params; } public String getLocalizedMessage(Locale locale) { //message would be something like "The %s foo'd the %s" //or of course "le %s foo'd le %s" (FYI: I don't speak French) String message = getLocalizedMessageForKey(localizationKey); return String.format(locale,message,params); } public String getLocalizedMessage() { return getLocalizedMessage(getDefaultLocale()); } public String getMessage() { return getLocalizedMessage(); } }


  • Numberlink/Flow Game: How to spot NP-Complete problems?
  • Split string on comma and ignore comma in double quotes [duplicate]
  • C Standard Library Functions vs. System Calls. Which is `open()`?
  • SAS change date format
  • How can I overcome late binding in Active Directory search
  • Single inputstream containing two files. I want to split those files
  • spark in python: creating an rdd by loading binary data with numpy.fromfile
  • Ternary Best Practice
  • Chromedriver works manually but fails when ran from Jenkins Slave
  • Conflict. The container name “/gitlab-runner” is already in use by container
  • Applescript: Check if computer is playing any sound
  • C++ method declaration including a macro
  • How to get Attachment value from “$File” Item? using C# (Lotus Notes)
  • Need reference code for SMO in C# SQL Server 2008
  • Random number of FORM fields being prepared for database
  • How to display asterisk for input in Java? [duplicate]
  • Velocity (VM) template request parameters: Getting GET variables
  • How to use template selector within a ContentPresenter in Windows 8.1
  • Python List of Tuples (Find value with key + check if exist)
  • What's the difference between using RDFS/OWL and XML?
  • the IBM_JAVA error for running jobs in Hadoop 2.2.0
  • MVC3 Extension for ValidatorMessage
  • How to implement JQuery confirm dialog with JSF
  • iOS App crash issue `[UIWindow warpPoint:]`
  • Threads and Concurrent Modification Exception working with a list
  • How to process remote XML files with XSLT
  • Query regarding com.jcraft.jsch.JSchException: UnknownHostKey: x.y.com. DSA key fingerprint is “ac:e
  • Authorize Attribute Authentication with Postman in Web Api
  • Java Collections.shuffle() weird behaviour [closed]
  • How to join two tables from different databases
  • r - input value by user to dataframe via shiny
  • Drag and drop unicode TText in DelphiXe4
  • Using redis as an LRU cache for postgres
  • php “page caching” solution suggestions for CMS Applications
  • Jersey serializes character value to ASCII equivalent numeric string
  • CAS 4 - Not able to retrieve the LDAP groups after successful authentication
  • What does the “id” field in an Android “Google Play Music” broadcast intent correspond to?
  • No OpKernel was registered to support Op 'Conv2D' with these attrs