usecase

package module
v1.3.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 19, 2023 License: MIT Imports: 10 Imported by: 74

README

Use Case Interactor

Build Status Coverage Status GoDevDoc Code lines Comments

This module defines generalized contract of Use Case Interactor to enable The Clean Architecture in Go application.

Clean Architecture

Why?

Isolating transport layer from business logic reduces coupling and allows better control on both transport and business sides. For example the application needs to consume AMQP events and act on them, with isolated use case interactor it is easy to trigger same action with HTTP message (as a part of developer tools).

Use case interactors declare their ports and may serve as a source of information for documentation automation.

This abstraction is intended for use with automated transport layer, for example see REST.

Usage

Input/Output Definitions
// Configure use case interactor in application layer.
type myInput struct {
    Param1 int    `path:"param1" description:"Parameter in resource path." multipleOf:"2"`
    Param2 string `json:"param2" description:"Parameter in resource body."`
}

type myOutput struct {
    Value1 int    `json:"value1"`
    Value2 string `json:"value2"`
}

Classic API
u := usecase.NewIOI(new(myInput), new(myOutput), func(ctx context.Context, input, output interface{}) error {
    var (
        in  = input.(*myInput)
        out = output.(*myOutput)
    )

    if in.Param1%2 != 0 {
        return status.InvalidArgument
    }

    // Do something to set output based on input.
    out.Value1 = in.Param1 + in.Param1
    out.Value2 = in.Param2 + in.Param2

    return nil
})

Generic API with type parameters

With go1.18 and later (or gotip) you can use simplified generic API instead of classic API based on interface{}.

u := usecase.NewInteractor(func(ctx context.Context, input myInput, output *myOutput) error {
    if in.Param1%2 != 0 {
        return status.InvalidArgument
    }

    // Do something to set output based on input.
    out.Value1 = in.Param1 + in.Param1
    out.Value2 = in.Param2 + in.Param2

    return nil
})
Further Configuration And Usage
// Additional properties can be configured for purposes of automated documentation.
u.SetTitle("Doubler")
u.SetDescription("Doubler doubles parameter values.")
u.SetTags("transformation")
u.SetExpectedErrors(status.InvalidArgument)
u.SetIsDeprecated(true)

Then use configured use case interactor with transport/documentation/etc adapter.

For example with REST router:

// Add use case handler to router.
r.Method(http.MethodPost, "/double/{param1}", nethttp.NewHandler(u))

Documentation

Overview

Package usecase defines use case interactor.

Index

Examples

Constants

View Source
const ErrInvalidType = sentinelError("invalid type")

ErrInvalidType is returned on port type assertion error.

Variables

This section is empty.

Functions

func As

func As(interactor Interactor, target interface{}) bool

As finds the first Interactor in Interactor's chain that matches target, and if so, sets target to that Interactor value and returns true.

An Interactor matches target if the Interactor's concrete value is assignable to the value pointed to by target.

As will panic if target is not a non-nil pointer to either a type that implements Interactor, or to any interface type.

Types

type Error

type Error struct {
	AppCode    int
	StatusCode status.Code
	Value      error
	Context    map[string]interface{}
}

Error is an error with contextual information.

func (Error) AppErrCode

func (e Error) AppErrCode() int

AppErrCode returns application level error code.

func (Error) Error

func (e Error) Error() string

Error returns error message.

func (Error) Fields

func (e Error) Fields() map[string]interface{}

Fields exposes structured context of error.

func (Error) Status

func (e Error) Status() status.Code

Status returns status code of error.

func (Error) Unwrap

func (e Error) Unwrap() error

Unwrap returns parent error.

type ErrorCatcher added in v0.1.4

type ErrorCatcher func(ctx context.Context, input interface{}, err error)

ErrorCatcher is a use case middleware that collects non empty errors.

func (ErrorCatcher) Wrap added in v0.1.4

Wrap implements Middleware.

type HasDescription

type HasDescription interface {
	Description() string
}

HasDescription declares description.

type HasExpectedErrors

type HasExpectedErrors interface {
	ExpectedErrors() []error
}

HasExpectedErrors declares errors that are expected to cause use case failure.

type HasInputPort

type HasInputPort interface {
	// InputPort returns sample of input value, e.g. new(MyInput).
	InputPort() interface{}
}

HasInputPort declares input port.

type HasIsDeprecated

type HasIsDeprecated interface {
	IsDeprecated() bool
}

HasIsDeprecated declares status of deprecation.

type HasName

type HasName interface {
	Name() string
}

HasName declares title.

type HasOutputPort

type HasOutputPort interface {
	// OutputPort returns sample of output value, e.g. new(MyOutput).
	OutputPort() interface{}
}

HasOutputPort declares output port.

type HasTags

type HasTags interface {
	Tags() []string
}

HasTags declares tags of use cases group.

type HasTitle

type HasTitle interface {
	Title() string
}

HasTitle declares title.

type IOInteractor added in v0.1.1

type IOInteractor struct {
	Interactor
	Info
	WithInput
	WithOutput
}

IOInteractor is an interactor with input and output.

func NewIOI added in v0.1.2

func NewIOI(input, output interface{}, interact Interact, options ...func(i *IOInteractor)) IOInteractor

NewIOI creates use case interactor with input, output and interact action function.

It pre-fills name and title with caller function.

Example
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/swaggest/usecase"
	"github.com/swaggest/usecase/status"
)

func main() {
	// Configure use case interactor in application layer.
	type myInput struct {
		Param1 int    `path:"param1" description:"Parameter in resource path." multipleOf:"2"`
		Param2 string `json:"param2" description:"Parameter in resource body."`
	}

	type myOutput struct {
		Value1 int    `json:"value1"`
		Value2 string `json:"value2"`
	}

	u := usecase.NewIOI(new(myInput), new(myOutput), func(ctx context.Context, input, output interface{}) error {
		var (
			in  = input.(*myInput)
			out = output.(*myOutput)
		)

		if in.Param1%2 != 0 {
			return status.InvalidArgument
		}

		// Do something to set output based on input.
		out.Value1 = in.Param1 + in.Param1
		out.Value2 = in.Param2 + in.Param2

		return nil
	})

	// Additional properties can be configured for purposes of automated documentation.
	u.SetTitle("Doubler")
	u.SetDescription("Doubler doubles parameter values.")
	u.SetTags("transformation")
	u.SetExpectedErrors(status.InvalidArgument)
	u.SetIsDeprecated(true)

	// The code below illustrates transport side.

	// At transport layer, input and out ports are to be examined and populated using reflection.
	// For example request body could be json unmarshaled, or request parameters can be mapped.
	input := new(myInput)
	// input := reflect.New(reflect.TypeOf(u.InputPort()))
	input.Param1 = 1234
	input.Param2 = "abc"

	output := new(myOutput)
	// output := reflect.New(reflect.TypeOf(u.OutputPort()))

	// When input is prepared and output is initialized, transport should invoke interaction.
	err := u.Interact(context.TODO(), input, output)
	if err != nil {
		log.Fatal(err)
	}

	// And make some use of prepared output.
	fmt.Printf("%+v\n", output)

}
Output:

&{Value1:2468 Value2:abcabc}

type IOInteractorOf added in v1.1.1

type IOInteractorOf[i, o any] struct {
	IOInteractor

	InteractFunc func(ctx context.Context, input i, output *o) error
}

IOInteractorOf is an IOInteractor with parametrized input/output types.

func NewInteractor added in v1.1.1

func NewInteractor[i, o any](interact func(ctx context.Context, input i, output *o) error, options ...func(i *IOInteractor)) IOInteractorOf[i, o]

NewInteractor creates generic use case interactor with input and output ports.

It pre-fills name and title with caller function. Input is passed by value, while output is passed by pointer to be mutable.

func (IOInteractorOf[i, o]) Invoke added in v1.1.1

func (ioi IOInteractorOf[i, o]) Invoke(ctx context.Context, input i, output *o) error

Invoke calls interact function in a type-safe way.

type Info

type Info struct {
	// contains filtered or unexported fields
}

Info exposes information about use case.

func (Info) Description

func (i Info) Description() string

Description implements HasDescription.

func (Info) ExpectedErrors

func (i Info) ExpectedErrors() []error

ExpectedErrors implements HasExpectedErrors.

func (Info) IsDeprecated

func (i Info) IsDeprecated() bool

IsDeprecated implements HasIsDeprecated.

func (Info) Name

func (i Info) Name() string

Name implements HasName.

func (*Info) SetDescription

func (i *Info) SetDescription(description string)

SetDescription sets use case description.

func (*Info) SetExpectedErrors

func (i *Info) SetExpectedErrors(expectedErrors ...error)

SetExpectedErrors sets errors that are expected to cause use case failure.

func (*Info) SetIsDeprecated

func (i *Info) SetIsDeprecated(isDeprecated bool)

SetIsDeprecated sets status of deprecation.

func (*Info) SetName

func (i *Info) SetName(name string)

SetName sets use case title.

func (*Info) SetTags

func (i *Info) SetTags(tags ...string)

SetTags sets tags of use cases group.

func (*Info) SetTitle

func (i *Info) SetTitle(title string)

SetTitle sets use case title.

func (Info) Tags

func (i Info) Tags() []string

Tags implements HasTag.

func (Info) Title

func (i Info) Title() string

Title implements HasTitle.

type Interact

type Interact func(ctx context.Context, input, output interface{}) error

Interact makes use case interactor from function.

func (Interact) Interact

func (i Interact) Interact(ctx context.Context, input, output interface{}) error

Interact implements Interactor.

type Interactor

type Interactor interface {
	// Interact sets output port value with regards to input port value or fails.
	Interact(ctx context.Context, input, output interface{}) error
}

Interactor orchestrates the flow of data to and from the entities, and direct those entities to use their enterprise wide business rules to achieve the goals of the use case.

func Wrap

func Wrap(interactor Interactor, mw ...Middleware) Interactor

Wrap decorates Interactor with Middlewares.

Having arguments i, mw1, mw2 the order of invocation is: mw1, mw2, i, mw2, mw1. Middleware mw1 can find behaviors of mw2 with As, but not vice versa.

type Middleware

type Middleware interface {
	Wrap(interactor Interactor) Interactor
}

Middleware creates decorated use case interactor.

type MiddlewareFunc

type MiddlewareFunc func(next Interactor) Interactor

MiddlewareFunc makes Middleware from function.

func (MiddlewareFunc) Wrap

func (mwf MiddlewareFunc) Wrap(interactor Interactor) Interactor

Wrap decorates use case interactor.

type OutputWithEmbeddedWriter

type OutputWithEmbeddedWriter struct {
	io.Writer
}

OutputWithEmbeddedWriter implements streaming use case output.

func (*OutputWithEmbeddedWriter) SetWriter

func (o *OutputWithEmbeddedWriter) SetWriter(w io.Writer)

SetWriter implements OutputWithWriter.

type OutputWithNoContent

type OutputWithNoContent struct {
	// contains filtered or unexported fields
}

OutputWithNoContent is embeddable structure to provide conditional output discard state.

func (OutputWithNoContent) NoContent

func (o OutputWithNoContent) NoContent() bool

NoContent returns output discard state.

func (*OutputWithNoContent) SetNoContent

func (o *OutputWithNoContent) SetNoContent(enabled bool)

SetNoContent controls output discard state.

type OutputWithWriter

type OutputWithWriter interface {
	SetWriter(w io.Writer)
}

OutputWithWriter defines output with streaming writer.

type WithInput

type WithInput struct {
	Input interface{}
}

WithInput is an embeddable implementation of HasInputPort.

func (WithInput) InputPort

func (wi WithInput) InputPort() interface{}

InputPort implements HasInputPort.

type WithOutput

type WithOutput struct {
	Output interface{}
}

WithOutput is an embeddable implementation of HasOutputPort.

func (WithOutput) OutputPort

func (wi WithOutput) OutputPort() interface{}

OutputPort implements HasOutputPort.

Directories

Path Synopsis
Package status defines a list of canonical statuses.
Package status defines a list of canonical statuses.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL