Age | Commit message (Collapse) | Author |
|
API changes were not adopted in the unit tests.
|
|
|
|
|
|
It is a very common pattern that some function needs to access the
database and wants to wrap all the actions into one transaction.
The advantage of a transaction is that it is ACID:
- atomic
- consistent
- isolated
- durable
In Go it is required to request a new transaction, execute functionality
on it and handle rollback or commit of this transaction based on the
success of the operation.
All this and the error handling can be written down in the
model.Transaction() function exactly once. The full signature of it is:
func Transaction(f func(*sql.Tx) error) error
It requires a function or closure passed as argument which takes the
transaction (*sql.Tx) and returns an error which might be nil.
This is very generic. It is applied to:
- injecting test data
- database migrations
- data read requests
- data write requests
|
|
|
|
A create, read, update or delete (CRUD) method should only care about
the object which provides the receiver and the relations to its child
objects.
For example the method
func (r *Recipe) Create(tx *sql.Tx) error {}
should only create the relational data inside the database for the
recipe, not for the steps nested into this Recipe struct. This should be
covered by the
func (s *Step) Create(tx *sql.Tx) error {}
method which is then called by `func (r *Recipe) Create()`.
This has the advantage that every CRUD method has a constraint scope and
is more unified since the Step CRUD methods now have a Step struct as
receiver instead of a Recipe receiver.
|
|
When nesting objects like steps into other objects like recipes it is
required to pass a *sql.Tx value to the CRUD methods of the inner
object to be able to roll back the whole transaction.
The top level object used to be responsible for the creation of this
*sql.Tx inside its CRUD methods.
This is now moved to the caller of the CRUD methods (here the HTTP
handler function). The advantage is that all CRUD methods now accept a
*sql.Tx as only argument which makes those methods more consistent.
|
|
This allows to print a recipe with a fmt.Printf() call more easily:
fmt.Printf("%s\n", recipe)
This is also used for better error output in unit tests with t.Fatalf().
The Stringer interface is implemented with the JSON package because an
indented version of a recipe is a useful string representation.
|
|
This allows to formulate the test data with an object-based model which
is easier than writing it down in a relational model.
|
|
The new Go type 'Recipe' should contain every information directly
related to a recipe. It should be sufficient to pass it to a template to
directly render a HTML view or edit page for the recipe or to a template
to generate a PDF.
The CRUD methods are:
- func (r *Recipe) Create() error
- func (r *Recipe) Update() error
- func (r *Recipe) Read() error
- func (r *Recipe) Delete() error
Together with the type itself they are the interface the model package
provides for recipes.
|