sabi

package module
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Sep 11, 2023 License: MIT Imports: 4 Imported by: 5

README

Sabi Go Reference CI Status MIT License

A small framework to separate logics and data accesses for Golang application.

Concepts

The concept of this framework is separation and reintegration of necessary and redundant parts based on the perspectives of the whole and the parts. The separation of logics and data accesses is the most prominent and fundamental part of this concept.

Separation of logics and data accesses

In general, a program consists of procedures and data. And procedures include data accesses for operating data, and the rest of procedures are logics. So we can say that a program consists of logics, data accesses and data.

We often think to separate an application to multiple layers, for example, controller layer, business logic layer, and data access layer. The logics and data accesses mentioned in this framework may appear to follow such layering. However, the controller layer also has data accesses such as transforming user requests and responses for the business logic layer. Generally, such layers of an application is established as vertical stages of data processing within a data flow.

In this framework, the relationship between logics and data accesses is not defined by layers but by lanes. Their relationship is vertical in terms of invocation, but is horizontal conceptually. DaxBase serves as an intermediary that connects both of them.

Separation of data accesses for each logic

A logic is a function that takes dax interface as its only one argument. The type of this dax is a type parameter of the logic function, and also a type parameter of the transaction function, Txn, that executes logics.

Therefore, since the type of dax can be changed for each logic or transaction, it is possible to limit data accesses used by the logic, by declaring only necessary data access methods from among ones defined in DaxBase instance..

At the same time, since all data accesses of a logic is done through this sole dax interface, this dax interface serves as a list of data access methods used by a logic.

Separation of data accesses by data sources and reintegration of them

Data access methods are implemented as methods of some Dax structs that embedding a DaxBase. Furthermore these Dax structs can be integrated into a single new DaxBase.

A Dax struct can be created in any unit, but it is clearer to create it in the unit of the data source. By doing so, the definition of a new DaxBase also serves as a list of the data sources being used.

Import declaration

To use this package in your code, the following import declaration is necessary.

import (
    "github.com/sttk/sabi"
    "github.com/sttk/sabi/errs"
)

Usage

Logic and an interface for its data access

A logic is implemented as a function. This function takes only an argument, dax, which is an interface that gathers only the data access methods needed by this logic function.

Since dax conceals details of data access procedures, this function only includes logical procedures. In this logical part, there is no concern about where the data is input from or where it is output to.

For example, in the following code, GreetLogic is a logic function and GreetDax is a dax interface for GreetLogic.

type ( // possible error reasons
    NoName struct {}
    FailToGetHour struct {}
    FailToOutput struct { Text string }
)

type GreetDax interface {
    UserName() (string, errs.Err)
    Hour() int
    Output(text string) errs.Err
}

func GreetLogic(dax GreetDax) errs.Err {
    hour, err := dax.Hour()
    if err.IsNotOk() {
      return err
    }

    var s string
    switch {
    case 5 <= hour && hour < 12:
      s = "Good morning, "
    case 12 <= hour && hour < 16:
      s = "Good afternoon, "
    case 16 <= hour && hour < 21:
      s = "Good evening, "
    default:
      s = "Hi, "
    }

    err = dax.Output(s)
    if err.IsNotOk() {
      return err
    }

    name, err := dax.UserName()
    if err.IsNotOk() {
      return err
    }

    return dax.Output(name)
}

In GreetLogic, there are no codes for inputting the hour, inputting a user name, and outputing a greeting. This function has only concern to create a greeting text.

Data accesses for unit testing

To test a logic function, the simplest dax struct is what using a map. The following code is an example of a dax struct using a map and having three methods that are same to GreetDax interface methods above.

type MapGreetDax struct {
    sabi.Dax
    m map[string]any
}

func (dax MapGreetDax) UserName() (string, errs.Err) {
    name, exists := dax.m["username"]
    if !exists {
        return "", errs.New(NoName{})
    }
    return name.(string), errs.Ok()
}

func (dax MapGreetDax) Hour() (int, errs.Err) {
    hour, exists := dax.m["hour"]
    if !exists {
        return 0, errs.New(FailToGetHour{})
    }
    return hour.(int), errs.Ok()
}

func (dax MapGreetDax) Output(text string) errs.Err {
    if m["greeting"] == "error" { // for testing the error case.
      return errs.New(FailToOutput{Text: text})
    }
    dax.m["greeting"] = text
    return errs.Ok()
}

func NewMapGreetDaxBase(m map[string]any) sabi.DaxBase {
    base := sabi.NewDaxBase()
    return struct {
      sabi.DaxBase
      MapGreetDax
    } {
      DaxBase: base,
      MapGreetDax: MapGreetDax{m: m},
    }
}

And the following code is an example of a test case.

func TestGreetLogic_morning(t *testing.T) {
    m := make(map[string]any)
    base := NewGreetDaxBase(m)

    m["username"] = "everyone"
    m["hour"] = 10
    err := sabi.Txn[GreetDax](base, GreetLogic)
    if err.IsNotOk() {
      t.Errorf(err.Error())
    }
    if m["greeting"] == "Good morning, everyone" {
      t.Errorf("Bad greeting: %v\n", m["greeting"])
    }
}
Data accesses for actual use

In actual use, multiple data sources are often used. In this example, an user name and the hour are input as command line argument, and greeting is output to console. Therefore, two dax struct are created and they are integrated into a new struct based on DaxBase. Since Golang is structural typing language, this new DaxBase can be casted to GreetDax.

The following code is an example of a dax struct which inputs an user name and the hour from command line argument.

type CliArgsDax struct {
    sabi.Dax
}

func (dax CliArgsDax) UserName() (string, errs.Err) {
    if len(os.Args) <= 1 {
      return "", errs.New(NoName{})
    }
    return os.Args[1], errs.Ok()
}

func (dax CliArgsDax) Hour() (string, errs.Err) {
    if len(os.Args) <= 2 {
      return 0, errs.New(FailToGetHour{})
    }
    return os.Args[2], errs.Ok()
}

The following code is an example of a dax struct which output a text to console.

type ConsoleDax struct {
    sabi.Dax
}

func (dax ConsoleDax) Output(text string) errs.Err {
    fmt.Println(text)
    return errs.Ok()
}

And the following code is an example of a constructor function of a struct based on DaxBase into which the above two dax are integrated. This implementation also serves as a list of the external data sources being used.

func NewGreetDaxBase() sabi.DaxBase {
    base := sabi.NewDaxBase()
    return struct {
        sabi.DaxBase
        CliArgsDax
        ConsoleDax
    } {
        DaxBase: base,
        CliArgsDax: CliArgsDax{Dax: base},
        ConsoleDax: ConsoleDax{Dax: base},
    }
}
Executing a logic

The following code executes the above GreetLogic in a transaction process.

func main() {
    if err := sabi.StartApp(app); err.IsNotOk() {
        fmt.Println(err.Error())
        os.Exit(1)
    }
}

func app() errs.Err {
    base := NewBase()
    defer base.Close()

    return sabi.Txn(base, GreetLogic))
}
Changing to a dax of another data source

In the above codes, the hour is obtained from command line arguments. Here, assume that the specification has been changed to retrieve it from system clock instread.

In this case, we can solve this by removing the Hour method from CliArgsDax and creating a new Dax, SystemClockDax, which has Hour method to retrieve a hour from system clock.

// func (dax CliArgsDax) Hour() (string, errs.Err) {  // Removed
//     if len(os.Args) <= 2 {
//       return 0, errs.New(FailToGetHour{})
//     }
//     return os.Args[2], errs.Ok()
// }

type SystemClockDax struct {  // Added
    sabi.Dax
}

func (dax SystemClockTimeDax) Hour() (string, errs.Err) {  // Added
    return time.Now().Hour(), errs.Ok()
}

And the DaxBase struct, into which multiple dax structs have been integrated, is modified as follows.

func NewGreetDaxBase() sabi.DaxBase {
    base := sabi.NewDaxBase()
    return struct {
        sabi.DaxBase
        CliArgsDax
        SystemClockDax  // Added
        ConsoleDax
    } {
        DaxBase: base,
        CliArgsDax: CliArgsDax{Dax: base},
        SystemClockDax: SystemClockDax{Dax: base},  // Added
        ConsoleDax: ConsoleDax{Dax: base},
    }
}
Moving outputs to next transaction process.

The above codes works normally if no error occurs. But if an error occurs at getting user name, a incomplete string is being output to console. Such behavior is not appropriate for transaction processing.

So we should change the above codes to store in memory temporarily in the existing transaction process, and output to console in the next transaction.

The following code is the implementation of MemoryDax which is memory store dax and the DaxBase struct after replacing ConsoleDax to MemoryDax.

type MemoryDax struct {  // Added
    sabi.Dax
    text string
}

func (dax *MemoryDax) Output(text string) errs.Err {  // Added
    dax.text = text
    return errs.Ok()
}

func (dax *MemoryDax) GetText() string {  // Added
    return dax.text
}

func (dax ConsoleDax) Print(text string) errs.Err {  // Changed from Output
    fmt.Println(text)
    return errs.Ok()
}

func NewGreetDaxBase() sabi.DaxBase {
    base := sabi.NewDaxBase()
    return struct {
        sabi.DaxBase
        CliArgsDax
        SystemClockDax
        ConsoleDax
        MemoryDax  // Added
    } {
        DaxBase: base,
        CliArgsDax: CliArgsDax{Dax: base},
        SystemClockDax: SystemClockDax{Dax: base},
        ConsoleDax: ConsoleDax{Dax: base},
        MemoryDax: MemoryDax{Dax: base},  // Added
    }
}

The following code is the logic to output text to console in next transaction process, the dax interface for the logic, and the execution of logics after being changed.

type PrintDax interface {  // Added
    GetText() string
    Print(text string) errs.Err
}

func PrintLogic(dax PrintDax) errs.Err {  // Added
    text := dax.GetText()
    return dax.Print(text)
}

func app() errs.Err {
    base := NewBase()
    defer base.Close()

    return sabi.Txn(base, GreetLogic)).    // Changed
        IfOk(sabi.Txn_(base, PrintLogic))  // Added
}

The important point is that the GreetLogic function is not changed. Since these changes are not related to the existing application logic, it is limited to the data access part (and the part around the newly added logic) only.

Supporting Go versions

This framework supports Go 1.18 or later.

Actual test results for each Go version:
% gvm-fav
Now using version go1.18.10
go version go1.18.10 darwin/amd64
ok  	github.com/sttk/sabi	0.614s	coverage: 100.0% of statements
ok  	github.com/sttk/sabi/errs	0.793s	coverage: 100.0% of statements

Now using version go1.19.13
go version go1.19.13 darwin/amd64
ok  	github.com/sttk/sabi	0.559s	coverage: 100.0% of statements
ok  	github.com/sttk/sabi/errs	0.755s	coverage: 100.0% of statements

Now using version go1.20.8
go version go1.20.8 darwin/amd64
ok  	github.com/sttk/sabi	0.566s	coverage: 100.0% of statements
ok  	github.com/sttk/sabi/errs	0.833s	coverage: 100.0% of statements

Now using version go1.21.1
go version go1.21.1 darwin/amd64
ok  	github.com/sttk/sabi	0.572s	coverage: 100.0% of statements
ok  	github.com/sttk/sabi/errs	0.835s	coverage: 100.0% of statements

Back to go1.21.1
Now using version go1.21.1

License

Copyright (C) 2022-2023 Takayuki Sato

This program is free software under MIT License.
See the file LICENSE in this distribution for more details.

Documentation

Overview

Package github.com/sttk/sabi is a small framework to separate logic parts and data access parts for Golang applications.

The concept of this framework is separation and reintegration of necessary and redundant parts based on the perspectives of the whole and the parts. The separation of logics and data accesses is the most prominent and fundamental part of this concept.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Close added in v0.6.0

func Close()

Close is the function that closes and frees each resource of registered global DaxSrc(s). This function should always be called before an application ends.

Example
package main

import (
	"os"

	"github.com/sttk/sabi"
	"github.com/sttk/sabi/errs"
)

type CliArgDaxSrc struct{}

func (ds CliArgDaxSrc) Setup(ag sabi.AsyncGroup) errs.Err {
	return errs.Ok()
}
func (ds CliArgDaxSrc) Close() {}
func (ds CliArgDaxSrc) CreateDaxConn() (sabi.DaxConn, errs.Err) {
	return nil, errs.Ok()
}

func NewCliArgDaxSrc(osArgs []string) CliArgDaxSrc {
	return CliArgDaxSrc{}
}

func main() {
	sabi.Uses("cliargs", NewCliArgDaxSrc(os.Args))

	err := sabi.Setup()
	if err.IsNotOk() {
		// ...
	}
	defer sabi.Close()

	// ...
}
Output:

func GetDaxConn

func GetDaxConn[C DaxConn](dax Dax, name string) (C, errs.Err)

GetDaxConn is the function to cast type of DaxConn instance. If the cast failed, this function returns an errs.Err of the reason: FailToCastDaxConn with the DaxConn name and type names of source and destination.

func Para

func Para(runners ...func() errs.Err) errs.Err

Para is the function which runs argument functions in parallel.

Example
package main

import (
	"github.com/sttk/sabi"
	"github.com/sttk/sabi/errs"
)

func NewMyDaxBase() sabi.DaxBase {
	return sabi.NewDaxBase()
}

func main() {
	type FooDax interface {
		sabi.Dax
		// ...
	}

	type BarDax interface {
		sabi.Dax
		// ...
	}

	base := NewMyDaxBase()

	txn1 := sabi.Txn_[FooDax](base, func(dax FooDax) errs.Err {
		// ...
		return errs.Ok()
	})

	txn2 := sabi.Txn_[BarDax](base, func(dax BarDax) errs.Err {
		// ...
		return errs.Ok()
	})

	err := sabi.Para(txn1, txn2)
	if err.IsNotOk() {
		// ...
	}
}
Output:

func Para_ added in v0.6.0

func Para_(runners ...func() errs.Err) func() errs.Err

Para_ is the function which creates a runner function which runs Para function.

func Seq

func Seq(runners ...func() errs.Err) errs.Err

Seq is the function which runs argument functions sequencially.

Example
package main

import (
	"github.com/sttk/sabi"
	"github.com/sttk/sabi/errs"
)

func NewMyDaxBase() sabi.DaxBase {
	return sabi.NewDaxBase()
}

func main() {
	type FooDax interface {
		sabi.Dax
		// ...
	}

	type BarDax interface {
		sabi.Dax
		// ...
	}

	base := NewMyDaxBase()

	txn1 := sabi.Txn_[FooDax](base, func(dax FooDax) errs.Err {
		// ...
		return errs.Ok()
	})

	txn2 := sabi.Txn_[BarDax](base, func(dax BarDax) errs.Err {
		// ...
		return errs.Ok()
	})

	err := sabi.Seq(txn1, txn2)
	if err.IsNotOk() {
		// ...
	}
}
Output:

func Seq_ added in v0.6.0

func Seq_(runners ...func() errs.Err) func() errs.Err

Seq_ is the function which creates a runner function which runs Seq function.

func Setup added in v0.6.0

func Setup() errs.Err

Setup is the function that make the globally registered DaxSrc usable.

This function forbids adding more global DaxSrc(s), and calls each Setup method of all registered DaxSrc(s). If one of DaxSrc(s) fails to execute synchronous Setup, this function stops other setting up and returns an errs.Err containing the error reason of that failure.

If one of DaxSrc(s) fails to execute asynchronous Setup, this function continue to other setting up and returns an errs.Err containing the error reason of that failure and other errors if any.

Example
package main

import (
	"os"

	"github.com/sttk/sabi"
	"github.com/sttk/sabi/errs"
)

type CliArgDaxSrc struct{}

func (ds CliArgDaxSrc) Setup(ag sabi.AsyncGroup) errs.Err {
	return errs.Ok()
}
func (ds CliArgDaxSrc) Close() {}
func (ds CliArgDaxSrc) CreateDaxConn() (sabi.DaxConn, errs.Err) {
	return nil, errs.Ok()
}

func NewCliArgDaxSrc(osArgs []string) CliArgDaxSrc {
	return CliArgDaxSrc{}
}

type DatabaseDaxSrc struct{}

func (ds DatabaseDaxSrc) Setup(ag sabi.AsyncGroup) errs.Err {
	return errs.Ok()
}
func (ds DatabaseDaxSrc) Close() {}
func (ds DatabaseDaxSrc) CreateDaxConn() (sabi.DaxConn, errs.Err) {
	return nil, errs.Ok()
}

func NewDatabaseDaxSrc(driverName, dataSourceName string) DatabaseDaxSrc {
	return DatabaseDaxSrc{}
}

var (
	driverName     string
	dataSourceName string
)

func main() {
	sabi.Uses("cliargs", NewCliArgDaxSrc(os.Args))
	sabi.Uses("database", NewDatabaseDaxSrc(driverName, dataSourceName))

	err := sabi.Setup()
	if err.IsNotOk() {
		// ...
	}
	defer sabi.Close()

	// ...
}
Output:

func StartApp added in v0.6.0

func StartApp(app func() errs.Err) errs.Err

StartApp is the function that calls Setup function, the argument function, and Close function in order. If Setup function or the argument function fails, this function stops calling other functions and return an errs.Err containing the error reaason..

This function is a macro-like function aimed at reducing the amount of coding.

Example
package main

import (
	"os"

	"github.com/sttk/sabi"
	"github.com/sttk/sabi/errs"
)

type CliArgDaxSrc struct{}

func (ds CliArgDaxSrc) Setup(ag sabi.AsyncGroup) errs.Err {
	return errs.Ok()
}
func (ds CliArgDaxSrc) Close() {}
func (ds CliArgDaxSrc) CreateDaxConn() (sabi.DaxConn, errs.Err) {
	return nil, errs.Ok()
}

func NewCliArgDaxSrc(osArgs []string) CliArgDaxSrc {
	return CliArgDaxSrc{}
}

type DatabaseDaxSrc struct{}

func (ds DatabaseDaxSrc) Setup(ag sabi.AsyncGroup) errs.Err {
	return errs.Ok()
}
func (ds DatabaseDaxSrc) Close() {}
func (ds DatabaseDaxSrc) CreateDaxConn() (sabi.DaxConn, errs.Err) {
	return nil, errs.Ok()
}

func NewDatabaseDaxSrc(driverName, dataSourceName string) DatabaseDaxSrc {
	return DatabaseDaxSrc{}
}

var (
	driverName     string
	dataSourceName string
)

func main() {
	sabi.Uses("cliargs", NewCliArgDaxSrc(os.Args))
	sabi.Uses("database", NewDatabaseDaxSrc(driverName, dataSourceName))

	app := func() errs.Err {
		// ...
		return errs.Ok()
	}

	err := sabi.StartApp(app)
	if err.IsNotOk() {
		// ...
	}
}
Output:

func Txn

func Txn[D any](base DaxBase, logics ...func(dax D) errs.Err) errs.Err

Txn is the function that executes logic functions in a transaction.

First, this function casts the argument DaxBase to the type specified with the function's type parameter. Next, this function begins the transaction, and the argument logic functions are executed.. Then, if no error occurs, this function commits all updates in the transaction, otherwise rollbacks them. If there are commit errors after some DaxConn(s) are commited, or there are DaxConn(s) which don't have rollback mechanism, this function executes ForceBack methods of those DaxConn(s). And after that, this function ends the transaction.

During a transaction, it is denied to add or remove any local DaxSrc(s).

Example
package main

import (
	"github.com/sttk/sabi"
	"github.com/sttk/sabi/errs"
)

type CliArgOptionDax struct {
	sabi.Dax
}

type DatabaseSetDax struct {
	sabi.Dax
}

type HttpReqParamDax struct {
	sabi.Dax
}

type HttpRespOutputDax struct {
	sabi.Dax
}

func NewMyBase() sabi.DaxBase {
	base := sabi.NewDaxBase()
	return &struct {
		sabi.DaxBase
		CliArgOptionDax
		DatabaseSetDax
		HttpReqParamDax
		HttpRespOutputDax
	}{
		DaxBase:           base,
		CliArgOptionDax:   CliArgOptionDax{Dax: base},
		DatabaseSetDax:    DatabaseSetDax{Dax: base},
		HttpReqParamDax:   HttpReqParamDax{Dax: base},
		HttpRespOutputDax: HttpRespOutputDax{Dax: base},
	}
}

func main() {
	base := NewMyBase()
	defer base.Close()

	type GetSetDax struct {
		// ...
	}

	GetSetLogic := func(dax GetSetDax) errs.Err {
		// ...
		return errs.Ok()
	}

	err := sabi.Txn(base, GetSetLogic)
	if err.IsNotOk() {
		// ...
	}
}
Output:

func Txn_ added in v0.6.0

func Txn_[D any](base DaxBase, logics ...func(dax D) errs.Err) func() errs.Err

Txn_ is the function that creates a runner function which runs a Txn function.

func Uses added in v0.6.0

func Uses(name string, ds DaxSrc) errs.Err

Uses is the method that registers a global DaxSrc with its name to enable to use DaxConn created by the argument DaxSrc in all transactions.

If a DaxSrc is tried to register with a name already registered, it is ignored and a DaxSrc registered with the same name first is used. And this method ignore adding new DaxSrc(s) after Setup or first beginning of Proc or Txn.

Example
package main

import (
	"os"

	"github.com/sttk/sabi"
	"github.com/sttk/sabi/errs"
)

type CliArgDaxSrc struct{}

func (ds CliArgDaxSrc) Setup(ag sabi.AsyncGroup) errs.Err {
	return errs.Ok()
}
func (ds CliArgDaxSrc) Close() {}
func (ds CliArgDaxSrc) CreateDaxConn() (sabi.DaxConn, errs.Err) {
	return nil, errs.Ok()
}

func NewCliArgDaxSrc(osArgs []string) CliArgDaxSrc {
	return CliArgDaxSrc{}
}

type DatabaseDaxSrc struct{}

func (ds DatabaseDaxSrc) Setup(ag sabi.AsyncGroup) errs.Err {
	return errs.Ok()
}
func (ds DatabaseDaxSrc) Close() {}
func (ds DatabaseDaxSrc) CreateDaxConn() (sabi.DaxConn, errs.Err) {
	return nil, errs.Ok()
}

func NewDatabaseDaxSrc(driverName, dataSourceName string) DatabaseDaxSrc {
	return DatabaseDaxSrc{}
}

var (
	driverName     string
	dataSourceName string
)

func main() {
	sabi.Uses("cliargs", NewCliArgDaxSrc(os.Args))
	sabi.Uses("database", NewDatabaseDaxSrc(driverName, dataSourceName))

	err := sabi.StartApp(func() errs.Err {
		// ...
		return errs.Ok()
	})
	if err.IsNotOk() {
		// ...
	}
	// ...
}
Output:

Types

type AsyncGroup added in v0.6.0

type AsyncGroup interface {
	Add(fn func() errs.Err)
}

AsyncGroup is the interface to execute added functions asynchronously. The method: Add is to add target functions. This interface is used as an argument of DaxSrc#Setup, DaxConn#Commit, and DaxConn#Rollback.

Example
package main

import (
	"github.com/sttk/sabi"
	"github.com/sttk/sabi/errs"
)

type SyncDaxSrc struct{}

func (ds SyncDaxSrc) Setup(ag sabi.AsyncGroup) errs.Err {
	// ...
	return errs.Ok()
}

func (ds SyncDaxSrc) Close() {}
func (ds SyncDaxSrc) CreateDaxConn() (sabi.DaxConn, errs.Err) {
	return nil, errs.Ok()
}

type AsyncDaxSrc struct{}

func (ds AsyncDaxSrc) Setup(ag sabi.AsyncGroup) errs.Err {
	ag.Add(func() errs.Err {
		// ...
		return errs.Ok()
	})
	return errs.Ok()
}

func (ds AsyncDaxSrc) Close() {}
func (ds AsyncDaxSrc) CreateDaxConn() (sabi.DaxConn, errs.Err) {
	return nil, errs.Ok()
}

func main() {
	sabi.Uses("sync", SyncDaxSrc{})
	sabi.Uses("async", AsyncDaxSrc{})

	err := sabi.Setup()
	if err.IsNotOk() {
		return
	}
}
Output:

type CreatedDaxConnIsNil added in v0.6.0

type CreatedDaxConnIsNil struct {
	Name string
}

CreatedDaxConnIsNil is the error reason which indicates that a DaxSrc created a DaxConn instance but it is nil. The field Name is the registered name of the DaxSrc that created a nil DaxConn.

type Dax

type Dax interface {
	// contains filtered or unexported methods
}

Dax is the interface for a set of data access methods.

This interface is embedded by Dax implementations for data stores, and each Dax implementation defines data access methods to each data store. In data access methods, DacConn instances connected to data stores can be got with GetConn function.

At the same time, this interface is embedded by Dax interfaces for logics. The each Dax interface declares only methods used by each logic.

type DaxBase

type DaxBase interface {
	Dax

	Close()
	Uses(name string, ds DaxSrc) errs.Err
	Uses_(name string, ds DaxSrc) func() errs.Err
	Disuses(name string)
	Disuses_(name string) func() errs.Err
	// contains filtered or unexported methods
}

DaxBase is the interface that declares the methods to manage DaxSrc(s). And this interface declarees unexported methods to process a transaction.

Close is the method to close and free all local DaxSrc(s). Uses is the method to register and setup a local DaxSrc with an argument name. Uses_ is the method that creates a runner function which runs #Uses method. Disuses is the method to close and remove a local DaxSrc specified by an argument name. Disuses_ is the method that creates a runner function which runs #Disuses method.

Example
package main

import (
	"os"

	"github.com/sttk/sabi"
	"github.com/sttk/sabi/errs"
)

type CliArgDaxSrc struct{}

func (ds CliArgDaxSrc) Setup(ag sabi.AsyncGroup) errs.Err {
	return errs.Ok()
}
func (ds CliArgDaxSrc) Close() {}
func (ds CliArgDaxSrc) CreateDaxConn() (sabi.DaxConn, errs.Err) {
	return nil, errs.Ok()
}

func NewCliArgDaxSrc(osArgs []string) CliArgDaxSrc {
	return CliArgDaxSrc{}
}

type DatabaseDaxSrc struct{}

func (ds DatabaseDaxSrc) Setup(ag sabi.AsyncGroup) errs.Err {
	return errs.Ok()
}
func (ds DatabaseDaxSrc) Close() {}
func (ds DatabaseDaxSrc) CreateDaxConn() (sabi.DaxConn, errs.Err) {
	return nil, errs.Ok()
}

func NewDatabaseDaxSrc(driverName, dataSourceName string) DatabaseDaxSrc {
	return DatabaseDaxSrc{}
}

var (
	driverName     string
	dataSourceName string
)

type HttpRequestDaxSrc struct{}

func (ds HttpRequestDaxSrc) Setup(ag sabi.AsyncGroup) errs.Err {
	return errs.Ok()
}
func (ds HttpRequestDaxSrc) Close() {}
func (ds HttpRequestDaxSrc) CreateDaxConn() (sabi.DaxConn, errs.Err) {
	return nil, errs.Ok()
}

func NewHttpRequestDaxSrc(req any) HttpRequestDaxSrc {
	return HttpRequestDaxSrc{}
}

var req any

type HttpResponseDaxSrc struct{}

func (ds HttpResponseDaxSrc) Setup(ag sabi.AsyncGroup) errs.Err {
	return errs.Ok()
}
func (ds HttpResponseDaxSrc) Close() {}
func (ds HttpResponseDaxSrc) CreateDaxConn() (sabi.DaxConn, errs.Err) {
	return nil, errs.Ok()
}

func NewHttpResponseDaxSrc(resp any) HttpResponseDaxSrc {
	return HttpResponseDaxSrc{}
}

var resp any

type CliArgOptionDax struct {
	sabi.Dax
}

type DatabaseSetDax struct {
	sabi.Dax
}

type HttpReqParamDax struct {
	sabi.Dax
}

type HttpRespOutputDax struct {
	sabi.Dax
}

func main() {
	sabi.Uses("cliargs", NewCliArgDaxSrc(os.Args))
	sabi.Uses("database", NewDatabaseDaxSrc(driverName, dataSourceName))

	err := sabi.Setup()
	if err.IsNotOk() {
		// ...
	}
	defer sabi.Close()

	NewMyBase := func() sabi.DaxBase {
		base := sabi.NewDaxBase()
		return &struct {
			sabi.DaxBase
			CliArgOptionDax
			DatabaseSetDax
			HttpReqParamDax
			HttpRespOutputDax
		}{
			DaxBase:           base,
			CliArgOptionDax:   CliArgOptionDax{Dax: base},
			DatabaseSetDax:    DatabaseSetDax{Dax: base},
			HttpReqParamDax:   HttpReqParamDax{Dax: base},
			HttpRespOutputDax: HttpRespOutputDax{Dax: base},
		}
	}

	type GetSetDax struct {
		// ...
	}

	GetSetLogic := func(dax GetSetDax) errs.Err {
		// ...
		return errs.Ok()
	}

	type OutputDax struct {
		// ...
	}

	OutputLogic := func(dax OutputDax) errs.Err {
		// ...
		return errs.Ok()
	}

	base := NewMyBase()
	defer base.Close()

	err = base.Uses("httpReq", NewHttpRequestDaxSrc(req)).
		IfOk(sabi.Txn_(base, GetSetLogic)).
		IfOk(base.Disuses_("httpReq")).
		IfOk(base.Uses_("httpResp", NewHttpResponseDaxSrc(resp))).
		IfOk(sabi.Txn_(base, OutputLogic))
	if err.IsNotOk() {
		// ...
	}
}
Output:

func NewDaxBase

func NewDaxBase() DaxBase

NewDaxBase is the function that creates a new DaxBase instance.

Example
package main

import (
	"github.com/sttk/sabi"
)

type CliArgOptionDax struct {
	sabi.Dax
}

type DatabaseSetDax struct {
	sabi.Dax
}

type HttpReqParamDax struct {
	sabi.Dax
}

type HttpRespOutputDax struct {
	sabi.Dax
}

func main() {
	base0 := sabi.NewDaxBase()

	base := &struct {
		sabi.DaxBase
		CliArgOptionDax
		DatabaseSetDax
		HttpReqParamDax
		HttpRespOutputDax
	}{
		DaxBase:           base0,
		CliArgOptionDax:   CliArgOptionDax{Dax: base0},
		DatabaseSetDax:    DatabaseSetDax{Dax: base0},
		HttpReqParamDax:   HttpReqParamDax{Dax: base0},
		HttpRespOutputDax: HttpRespOutputDax{Dax: base0},
	}

	_ = base
}
Output:

type DaxConn

type DaxConn interface {
	Commit(ag AsyncGroup) errs.Err
	IsCommitted() bool
	Rollback(ag AsyncGroup)
	ForceBack(ag AsyncGroup)
	Close()
}

DaxConn is the interface that represents a connection to a data store, and defines methods: Commit, Rollback and Close to work in a transaction process.

Commit is the method for commiting updates in a transaction. IsCommitted is the method for check whether updates are already committed. Rollback is the method for rollback updates in a transaction. ForceBack is the method to revert updates forcely even if updates are already commited or this connection ooes not have rollback mechanism. If commting and rollbacking procedures are asynchronous, the argument AsyncGroup(s) are used to process them. Close is the method to close this connecton.

type DaxSrc

type DaxSrc interface {
	Setup(ag AsyncGroup) errs.Err
	Close()
	CreateDaxConn() (DaxConn, errs.Err)
}

DaxSrc is the interface that represents a data source which creates connections to a data store like database, etc. This interface declares three methods: Setup, Close, and CreateDaxConn.

Setup is the method to connect to a data store and to prepare to create DaxConn objects which represents a connection to access data in the data store. If the set up procedure is asynchronous, the Setup method is implemented so as to use AsyncGroup. Close is the method to disconnect to a data store. CreateDaxConn is the method to create a DaxConn object.

type DaxSrcIsNotFound

type DaxSrcIsNotFound struct {
	Name string
}

DaxSrcIsNotFound is the error reason which indicates that a specified DaxSrc is not found. The field Name is the registered name of the DaxSrc not found.

type FailToCastDaxBase added in v0.6.0

type FailToCastDaxBase struct {
	FromType, ToType string
}

FailToCastDaxBase is the error reason which indicates that a DaxBase instance failed to cast to the destination type. The field FromType is the type name of the DaxBase instance and the field ToType is the type name of the destination type.

type FailToCastDaxConn

type FailToCastDaxConn struct {
	Name, FromType, ToType string
}

FailToCastDaxConn is the error reason which indicates that a DaxConn instance of the specified name failed to cast to the destination type. The field Name and FromType is the name and type name of the DaxConn instance, and the field ToType is the type name of the destination type.

type FailToCommitDaxConn

type FailToCommitDaxConn struct {
	Errors map[string]errs.Err
}

FailToCommitDaxConn is the error reason which indicates that some connections failed to commit. The field Errors is the map of which keys are registered names of DaxConn which failed to commit, and of which values are errs.Err(s) having their error reasons.

type FailToCreateDaxConn

type FailToCreateDaxConn struct {
	Name string
}

FailToCreateDaxConn is the error reason which indicates that it is failed to create a new connection to a data store. The field Name is the registered name of the DaxSrc failed to create a DaxConn.

type FailToRunInParallel

type FailToRunInParallel struct {
	Errors map[int]errs.Err
}

FailToRunInParallel is an error reason which indicates that some of runner functions running in parallel failed.

type FailToSetupGlobalDaxSrcs added in v0.6.0

type FailToSetupGlobalDaxSrcs struct {
	Errors map[string]errs.Err
}

FailToSetupGlobalDaxSrcs is the error reason which indicates that some DaxSrc(s) failed to set up. The field Errors is the map of which keys are the registered names of DaxSrc(s) that failed, and of which values are errs.Err having their error reasons.

type FailToSetupLocalDaxSrc added in v0.6.0

type FailToSetupLocalDaxSrc struct {
	Name string
}

FailToSetupLocalDaxSrc is the error reason which indicates that a local DaxSrc failed to set up. The field Name is the registered name of the DaxSrc failed.

Directories

Path Synopsis
errs is the package for error processing for sabi framework.
errs is the package for error processing for sabi framework.

Jump to

Keyboard shortcuts

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