opcotech/elemo

View on GitHub
docs/ADRs/0005.data-validation.md

Summary

Maintainability
Test Coverage
# Data Validation

| author       | created at | updated at | status   |
|:-------------|:-----------|------------|:---------|
| @gabor-boros | 2023-03-17 | -          | accepted |

## Abstract

[Vertex And Edge IDs] and [Database Schema Validation] introduced how vertices
and edges are going to be identified in the application, and how the database
schema is going to be validated. However, there is a third aspect of validation
which is not covered by the previous ADRs: the validation of the data itself.

This ADR describes how we are going to validate the data in the application. We
have to solve validation on two levels:

1. The data coming from the user needs to be validated in the transport layer.
2. The data needs to be validated before it is stored in the database.

We are caring about the second point for this ADR.

Although the two looks very similar, they are not the same. In the first case,
we are going to partially rely on the transport layer to validate the data it
receives. This could be done by using an OpenAPI schema-aware code generator,
or by using a framework which supports OpenAPI schemas.

[Vertex And Edge IDs]: 0003.vertex-and-edge-ids.md

[Database Schema Validation]: 0004.database-schema-validation.md

## Decision

In the second case, we are going to use the [validator] package to validate the
data before it is stored in the database. This package is going to be used in
almost all layers of the application, because the data may be transformed in
many ways before it is stored in the database.

By using the validator package, we are going to extend the struct tags with
the `validate` tag. This tag is going to be used to define the validation rules
for the fields of the struct.

In the example below, we can see how the `validate` tag is used to define the
validation rules for the `Name` field of the `Project` struct.

**Example:**

```go

var validate *validator.Validate

type Project struct {
    ID   string `validate:"omitempty,lowercase,alphanum"`
    Code string `validate:"required,min=2,max=6,alpha"`
    Name string `validate:"required,min=1,max=120"`
}

func init() {
    validate = validator.New()
}

func caller() {
    project := &Project{
        ID:   "9m4e2mr0ui3e8a215n4g",
        Code: "ABC",
        Name: "My Project",
    }

    if err := validate.Struct(project); err != nil {
        // handle error
    }
}
```

[validator]: https://github.com/go-playground/validator

## Consequences

By using the validator package, we are going to be able to validate the data
before it is stored in the database. This is going to be a huge advantage, as
we are going to be able to catch errors early, and we don't need to write our
own validation logic.

The downside of this approach is using the validator package in almost all
layers of the application. This is not a problem, because the package is
lightweight, commonly used in the community, maintained, and it is not going to
add any significant overhead to the application.

## References

* [OAPI Code Generator](https://github.com/deepmap/oapi-codegen)