Structuring business logic

Hi,

I have a mid-sized web service and I like the approach of having as most as possible functions and not service classes like in other languages.

Since I want to avoid implicit dependencies inside the function I have to provide some data e. g. an open db transaction.

My solution by now is that there is a middleware which creates a context with a transaction and passes this to the actual handler. This handler again passes it through to the business logic. There are some interfaces which define the contexts e. g.

type TransactionalCtx interface {
    GetTx() Transaction
}
type AnotherCtx interface {
    GetSthElse() SthElse
}

It works so far but the downsides are obvious:

  • One more argument in almost each and every function
  • I have to know what the functions need before I call them
  • What if I need a combination of different contextes in one function? I would need to create a “synthetic” interface for just one purpose.

Another idea I had was to create methods on a context. E. g.

type TransactionCtxImpl struct {
    tx Transaction
}

func (ctx *TransactionCtxImpl) GetTx() Transaction {
    return ...
}

func (ctx *TransactionCtxImpl) CreateUser(...) {
    ctx.GetTx().Insert(...)
}

But here we have the same problem with combinations.

Does anyone know a better approach for this? In fact it is not related to Golang itself but I ran into these problems and I am pretty sure that I am not the first one :slight_smile:

Have you thought of using an empty interface and checking for what type of context you have within your function?

Empty Interface

In fact I use interfaces. What is the benefit of checking the contexts inside of a function which needs it? Then it is already too late, if sth. is missing :slight_smile:

And I try to avoid using reflections and assertions where ever it is possible.

In Scala you can use Type hintings like “Interface A with Interface B” - Unfortunately here this is not possible.

Maybe this is not what you mean, but you can

type intfAandB interface {
    interfaceA
    interfaceB
}

function needsAandB(v intfAandB) {
    // ...
}

for the function that needs something that implements both…

Hi Calmh,

this is exactly one of the problems :slight_smile: Everytime you have a special combination of needs you would need create a new interface.

Actually this is just a little price but it feels… fiddly

Yeah. So far, for me, this is a very unusual occurrence. If every function requires a different interface there is not much in common between them. But your situation may be different, I don’t know.

You can of course express it directly in the function signature, like I guess you would in Scala, but it gets a bit verbose and I wouldn’t recommend it.

func foo(v interface {
	io.Reader
	fmt.Stringer
}) {
	v.Read(nil)
	fmt.Println(v.String())
}
1 Like

Hey,

at first glance this solution looks awesome. I did not know that one can use anonymous interfaces Why would you not recommend it?

I find the function signature annoyingly verbose and would rather declare a named type for the purpose, but by all means go ahead. This is just a personal preference, not a rule of any kind. Anonymous types are nice for one off variables though.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.