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