Skip to content

Issues with injecting river.Client dependency #317

@hypnoglow

Description

@hypnoglow

Consider the following app structure (code is simplified):

// database layer component, depends on conn pool and river to insert jobs transactionally
store := postgres.NewStore(db, riverClient)

// ...

// Worker implementation that uses store
worker := some.NewWorker(store)

// ...

// River client which depends on workers
riverClient := river.NewClient(river.Config{Workers: workers})

As you see, there is a dependency injection loop here: store depends on river client to insert jobs, but river client depends on workers, and worker depends on store.

I believe this situation is not unique and should be quite common for many apps.

One solution we can use currently is set river client into store later, like this:

riverClient := river.NewClient(river.Config{Workers: workers})

store.SetRiverClient(riverClient)

But this is a step away from dependency injection principles, as constructor should be sufficient.

Another solution that comes to mind is to use two clients:

// This client is used to insert jobs
riverClient := river.NewClient(river.Config{Workers: nil})

store := postgres.NewStore(db, riverClient)

// This client manages workers
riverWorkersClient := river.NewClient(river.Config{Workers: workers})

riverWorkersClient.Start()

The drawback is we losing validation.

Maybe I'm not seeing a better solution to this, could you suggest?

I guess this problem is a consequence of the fact that river.Client is too thick. Maybe it would be reasonable to separate job producer from workers, so the first component is responsible for creating jobs, while the second one is all about managing work. This way we can properly solve dependency issues, in fact, similar projects (like asynq already designed the API this way.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions