Should a Service always be an interface ?

199 views
Skip to first unread message

Wade Arnold

unread,
May 1, 2018, 9:34:04 AM5/1/18
to Go kit
I am starting a new project and am considering making my Service a concrete implementation rather than an interface. The examples have a Service defined as an interface. Enabling a concrete Service to be implemented such as inmemService in the example/profileSvs or basicService in example/addsvc. The FAQ even states "In Go kit, services are typically modeled as interfaces, and implementations of those interfaces contain the business logic" This pattern makes sense to me if the concrete service mostly calls to a storage layer and doesn't contain a lot of business logic. 
I find that my services have a lot of business logic and when I make my persistence abstraction at the service layer I have to repeat the business logic across each of the concrete implementations. Even simple service features such as generating a UUID or adding timestamps need to be replicated in each implementation.  

I find myself defining a Service interface, a single concrete implementation of the Service, a persistence interface DataStore, and several concrete implementations of a DataStore (in-memory, Postgres, Cloud SQL). This allows me to keep all the business logic consistent and just wire in a different store for testing or production. 

It ends up looking like this:

pgStore, err := NewPostgresStore("postgres://user:pass@localhost/bookstore")
if err != nil { log.Panic(err) }
s = profilesvc.NewService(pgStore)
s = profilesvc.LoggingMiddleware(logger)(s)
.... etc


Using this pattern the Service Interface and the DataStore interface are practically identical. 

I am starting a new project and I am thinking about defining the Service as a concrete implementation rather than an interface and use a DataStore interface to abstract the persistence layer. This allows me to have my business logic in one place and only abstract the CRUD commands. I wanted to ask the group if they run into the same interface/concrete/interface/concrete pattern or if I am going about abstracting my persistence layer wrong? Am I missing something that makes me want to remove the Service interface from the solution? 

Thanks for your feedback! 
Wade Arnold

Peter Bourgon

unread,
May 1, 2018, 1:51:12 PM5/1/18
to Wade Arnold, Go kit
The reason to define a service interface is to enable the creation of
so-called service middlewares. Service middlewares are implementations
of your service that perform a single responsibility (logging,
instrumentation, audit trail reporting, etc.) and pass the thread of
execution to a wrapped service interface. In this way, the "core"
concrete implementation of your service can perform only the core
business logic, including things like interacting with
storage/persistence layers.

It's generally unusual for the store/repository type to look similar
to the service type, i.e. have a similar set of methods. Normally, the
repository is a CRUD-oriented data access layer, and the service
provides a higher-level, user- or goal-oriented API. The profilesvc is
somewhat atypical in that the user-oriented API is approximately a
CRUD-oriented API. I think that is an anomaly; I probably wouldn't try
to make design decisions on the assumption that this is true across
most/all services.

Hope this helps,
Peter.
> --
> You received this message because you are subscribed to the Google Groups
> "Go kit" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to go-kit+un...@googlegroups.com.
> To post to this group, send email to go-...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/go-kit/4b37b7ec-89b1-4717-b365-b067c57db197%40googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Wade Arnold

unread,
May 1, 2018, 3:11:28 PM5/1/18
to Go kit
Peter thank you for the added context in your response - really appreciate it! 
Reply all
Reply to author
Forward
0 new messages