DDD, service and localization best practices?

I’m building HTTP API with DDD principles. The end goal is that the server runs on some chosen language X but the clients can use what ever language the software supports. Service has state, but HTTP is stateless.

First without language:

storage = new MyStorage(params) // Can be anything from FS based to SQL, etc
service = new TODO(storage)
server = new build(service)
server.run()

build class is something like:

class build
  server: HTTPBase

  func build(service) 
    server.POST('/add', 
      func(req, resp)
        what = req.Get("param")
        res = service.DoSomething(what)
        if res == "Y"
          resp.Write("Everything went fine")
          return
        resp.Write("Oh noes!")
    )

    return server

Let’s add a language (the wrong way with global state):

defaultLanguage = "eng"
storage = new MyStorage(defaultLanguage, params) 
service = new TODO(defaultLanguage, storage)
server = new build(service) // Oh noes!
server.run()

class build
  service
  server: HTTPBase

  func getLanguageFromURL(req, resp)
    self.service.SetLanguage(req.GetFromURL('$language'))

  func build(service)
    self.service = service // Oh noes!

    server.BaseRoute('/$language')
    server.Middleware(self.getLanguageFromURL)

    server.POST('/add', // This is now /$language/add
      func(req, resp)
        what = req.Get("param")
        // Because we're setting service's language in the middleware 
        // this becomes a mess because some other user using some other 
        // language might just done something and results to client then 
        // getting the result in wrong language.
        // (= This changes service's global state, and explodes)
        res = self.service.DoSomething(what)
        if res == "Y"
          resp.Write(translate("Everything went fine"))
          return
        resp.Write(translate("Oh noes!"))
    )

    return server

So where to go?

Always spawn new service in middleware?

defaultLanguage = "eng"
server = new build(defaultLanguage)
server.run()

class build
  service
  defaultLanguage
  server: HTTPBase

  func serviceMW(req, resp)
    storage = new MyStorage(self.defaultLanguage, params) 
    service = new TODO(self.defaultLanguage, storage)
    service.setLanguage(req.GetFromURL('$language'))
    req.Context['service'] = service


  func build(defaultLanguage)
    self.defaultLanguage = defaultLanguage
    server.BaseRoute('/$language')
    server.Middleware(serviceMW)

    server.POST('/add', // This is now /$language/add
      func(req, resp)
        // Now we have one time only service which is destroyed after request is complete
        service = req.Context.Get('service')
        what = req.Get("param")
        
        res = service.DoSomething(what)
        if res.X == "Y"
          resp.Write(translate("Everything went fine"))
          return
        resp.Write(translate("Oh noes!"))
    )

    return server

This is nicely isolated but starting the service might be really slow and eat lot of resources depending what it does.

Clone/copy the service in middleware?

defaultLanguage = "eng"
storage = new MyStorage(defaultLanguage, params) 
service = new TODO(defaultLanguage, storage)
server = new build(service)
server.run()

class build
  service // main instance which gets cloned for each request
  server: HTTPBase

  func serviceMW(req, resp)
    // Create new instance
    service = self.service.clone() 
    service.setLanguage(req.GetFromURL('$language'))
    req.Context['service'] = service

  func build(service)
    // Create new instance
    self.service = service.clone()

    server.BaseRoute('/$language')
    server.Middleware(serviceMW)

    server.POST('/add', // This is now /$language/add
      func(req, resp)
        what = req.Get("param")

        // Cloned in middleware and destroyed after this request
        service = req.Context.Get('service')

        res = service.DoSomething(what)
        if res == "Y"
          resp.Write(translate("Everything went fine"))
          return
        resp.Write(translate("Oh noes!"))
    )

    return server

Adds isolation and removes possible startup slowdowns and resource hogs as they’re done beforehand.

Run a pool (array) of services for each language?

class build
  service[language] // service["eng"], service["fin"], ..

There’s a lot of languages so it increases resource consumption. There could be also a mechanism that spawns service for a specific language when it’s requested for the first time. Also service’s that have been idle for N hours/minutes could be removed.

Add language parameter to service’s and repository’s functions?

All Service.DoSomething(param1) becomes Service.DoSomething(language, param1).

Services/repositories never return only strings but some sort of translatable objects?

So you have something like:

result = service.DoSomething(param1)
actualResult = result.Translate(myChosenLanguage)

What is the DDD way of handling localization? Am I missing any options?

Go to Source
Author: raspi

DDD: accessing data from external service (REST, SOAP, etc.) as part of domain

I have a situation where my business logic depends on data coming from an external service. I initially though this would be a prime candidate for a domain service, but now I’m starting to wonder if that’s even needed. With years of working mostly with relational database for fetching data, I think my brain may be stuck in a pattern that’s not really helpful. Theoretically, whether the data comes from a database or other SOAP/external service doesn’t matter, right? What matters is if this is a key part of the aggregate, which I believe it is. There is also database data that’s relevant, so it’s a mix.

My guess is that while the domain service could technically be OK, I could also create a repository that fetches the data from both the database and the external service, building my aggregate (probably using a factory to be safe). I can then encapsulate all the business logic within the aggregate itself instead of using a domain service.

The disadvantage of this is that my business object is now somewhat coupled to an external service (I say somewhat because technically the object is a plain object, but there’d be no way of constructing one in the context of the app without using the external service), but the fact of the matter is that it needs to be coupled, there’s no way for the behavior/methods I need for this object to work if the external service is not up. It’s critical to enforcing business rules.

So is there anything wrong with this approach that I’m not thinking of? Any major drawback that would mean using a domain service is a better approach? My assumption would be I’d need a domain service if whatever I need to do has to involve multiple aggregates, but if not I can keep it within the aggregate itself.

Go to Source
Author: Rocket04