Entities
In this section we can focus a little bit more on entities and how to define them in your database schema. You've already seen a few examples of entities in the previous sections, so let's dive deeper into the topic. Here's an example definition of user entity:
const val domainPackage = "com.example.user"
typealias UserId = String
@Definition(
domainPackage = domainPackage,
infrastructurePackage = "$domainPackage.infra",
apiPackage = "$domainPackage.api",
versions = [
DefinitionVersion(
version = V_1_0_0,
name = "users",
properties = [
Property(name = "id", type = SERIAL, mappedTo = UserId::class, default = "nanoid()"),
Property(name = "name", type = VARCHAR, details = "16"),
],
constraints = [
Constraint(type = PRIMARY_KEY, name = "pk_id", on =["id"]),
],
indices = [
Index(type = UNIQUE_INDEX, name = "uq_name", columns = ["name"])
]
),
DefinitionVersion(
version = V_1_0_1,
properties = [
Property(operation = RETYPE, name = "name", type = VARCHAR, details = "24"),
Property(operation = ADD, name = "display_name", type = VARCHAR, details = "48", nullable = true),
],
indices = [
Index(operation = REMOVE_INDEX, type = INDEX, name = "idx_id"),
Index(type = INDEX, name = "idx_id", columns = ["id"])
]
),
DefinitionVersion(
version = V_1_0_2,
properties = [
Property(operation = RENAME, name = "display_name", rename = "displayName")
]
)
]
)
object UserDefinition
Let's break it down.
Output packages
First of all, you can define output packages for generated classes:
domainPackage
- package for domain layer of your applicationinfrastructurePackage
- package for infrastructure layer of your applicationapiPackage
- package for API layer of your application
This is optional, by default it generates all classes in the same package as where you've defined your @DefinitionVersion
.
Serial ID
In most cases, your entities have their own autogenerated ID, that's why we're using SERIAL
type for the id
property. By default, it'd be represented by auto-incremented integer.
To make this a bit more complicated, let's say we want to use custom nanoid()
function to generate our IDs. Unfortunately, Sqiffy can't detect this type automatically, so we have to enforce it by using mappedTo
parameter:
Property(name = "id", type = SERIAL, mappedTo = UserId::class, default = "nanoid()"),
The UserId
is a type alias for String
, and that's what Sqiffy will use to represent the id
property in the generated API.
Initial version
In the initial version of our schema, we have to define the table name and its properties. If you don't want to use application-side schema versioning (like Sqiffy/Liquibase migrations), you will only need this version.
Note: Keep in mind that that during the initial development process of your application, versioning might not be necessary. To keep it simple, it's usually easier to recreate the database from scratch or update it manually.
Versioning
Once the initial version is done, and you're willing to change your schema, all you need to do is to add a new DefinitionVersion
to your schema definition. In further definitions, you're only declaring changes you want to apply to the previous version, not a whole schema.
In the example above, we've added two more versions to our UserDefinition
:
V_1_0_1
- we've changed the type ofname
property fromVARCHAR(16)
toVARCHAR(24)
, and added a newdisplay_name
propertyV_1_0_2
- we've renameddisplay_name
todisplayName
All other properties and constraints remain the same as in the previous version. Other supported operations:
Category | Operations |
---|---|
Property | * ADD - change the type of the property* RENAME - change the type of the property* RETYPE - change the type of the property* REMOVE - change the type of the property |
Constraint | * ADD_CONSTRAINT - add a new constraint* REMOVE_CONSTRAINT - remove a constraint |
Index | * ADD_INDEX - add a new index* REMOVE_INDEX - remove an index |
References
The last thing we want to show you is how to reference other entities in your schema. This is pretty straightforward, you just need to define constriants with FOREIGN_KEY
type and reference the other entity's object class:
@Definition([
DefinitionVersion(
version = V_1_0_0,
name = "guilds",
properties = [
Property(name = "id", type = SERIAL),
Property(name = "owner", type = INT),
],
constraints = [
Constraint(
type = FOREIGN_KEY,
name = "fk_guild_owner",
on = ["owner"],
referenced = UserDefinition::class,
references = "id"
)
]
),
])
object GuildDefinition
That's all!