Sunday 13 July 2014

Using expressive database id types with Scala and Slick

Background


Assume a many-to-one relationship between Traders and the Company on whose behalf they are trading. This means that both the Trader id and the Company id must be recorded with each transaction.

Consider this common class of error:












You probably spotted that traderId and companyId were switched on the call to debitAccount but it might not be so easy to see in a big codebase and this is an insidious little error which could leave your data in an inconsistent state.

Because this example uses two locally declared methods, having the parameters in the wrong order is poor coding style and should (anyway) be fixed by correcting the method signatures but if these methods were on different interfaces developed by different teams and the Market implementation was orchestrating calls across these service interfaces, a parameter ordering policy is less easily mandated (and is inherently unreliable anyway).

Enter the compiler. It lives to catch problems like this - but it needs type information to do its job.

That's easily provided












and of course, results in a compilation error.

The rest of this post proposes (continuing with the contrived example) a way to introduce this type safety while using Slick for persistence with Scala, where TraderId and CompanyId represent a database identity (primary key) - and the best bit is that by using Value classes, this can be achieved without additional overhead when compared to using the underlying raw type - Long in this case.

You can access the full code discussed in this post by cloning https://github.com/dextaa/blog.git. This repo contains multiple projects so go to the slick/typesafe-ids directory to access the root of this project. This is referred to as <BASE> from now on.

Generating custom Slick metadata from SBT


This is sufficient schema to illustrate the proposition.












This example uses MySQL throughout, although there's nothing that ties anything here to a certain DBMS.

Slick supports both the manual and auto-generation of the Scala schema metadata and it's possible to hand-craft the code using the more expressive types but as the metadata code can get complex, it's preferable to create an SBT task to create this boilerplate using the Slick source code generator.

Let's first examine the basic generator and the un-customised code.

























This creates an SBT build task which uses an un-customised Slick SourceCodeGenerator to generate the schema metadata. The only thing worth noting here is that the list of all of the tables in the database is filtered so that metadata is generated only for those tables listed in the IncludedTables sequence.

This SBT task is made available to the SBT build by adding 'slick <<= slickCodeGenTask' to the top level build.sbt which defines the project. The generation process can then be triggered by typing 'slick-gen' at the SBT prompt.

The generated code includes these artefacts, each of which represents the structure of a row in the associated table. These classes are used when reading or writing that table's data and use the Long datatype for ids.







To keep the ids expressively typed throughout the code, the typing would ideally start at this lowest level and permeate outwards. Artefacts which look like the following are what's required, so the SourceCodeGenerator in the SBT build task should be tweaked accordingly.







This tweaking takes the form of replacing the uncustomisedGenerator method shown above with the customisedGenerator method shown below and introducing the ColumnMappings Map which it references















ColumnMappings describes to the customised generator, which database table qualified column names should have their default Scala type changed to the type specified in the (value of) the mapping. The new type could be a fully qualified type name but the Slick SourceCodeGenerator allows subtypes to provide import statements to be included in the generated code, by overriding the code method.

In the code method shown above, com.github.dextaa.ids.model.api.types contains the CompanyId and TraderId types and com.github.dextaa.ids.model.slick.mapping.MySqlIdentity provides the type mappers which Slick requires. Both of these elements are examined below.

The SourceCodeGenerator provides hooks to override most aspects of the schema metadata using a hierarchical nesting of a table generator and column generator. There are many methods which could be customised, but here it's only necessary to override rawType, as it is used to define a column's Scala datatype in the associated Scala metadata.

The qualifiedTableName val is assigned the database table qualified name of the column currently being processed and is used to perform a look-up in ColumnMappings. If qualifiedTableName exists as a key in ColumnMappings, the matching value replaces the default type returned by the rawType method.

For example, where qualifiedTableName is buy.trader_id, rawType returns TraderId.

Assuming you pulled the code from GitHub, the full source for the SBT task is at
<BASE>/project/SlickMetadataBuild.scala and it writes to <BASE>/src/main/scala/com/github/dextaa/ids/model/slick/generated/SchemaMetadata.scala (there is an SBT 'generated' directory but it's not used as the write target in this example).

The required types are now referenced in the code and the generated artefacts look as they should but the types themselves and the associated mappers are yet to be examined. Read on.

The Type Mappers


The kind of type safety described by TraderId and CompanyId is exactly the kind of named type with zero additional overhead scenario for which Scala Value classes were added to the language in version 2.10, so TraderId and CompanyId are declared as value types.

The declarations are as simple as this




which means that the compiler simply uses the named type for compile time type checking and the underlying type (that is, the type of constructor parameter) at runtime.

Despite the underlying types being used at runtime, the Value class type is present at compile time so Slick still requires type mappers for the id types. They look like this.





















The MySqlIdentity object provides the MySQL specific versions of these mappers by defining a concrete Slick JDBC driver.

These mappers must be in implicit scope when using the new id types with Slick, so that Slick operators such as === will function correctly.

The Joda type mappers project was a useful reference when writing these mappers.

For this example, creating a new mapper for each new id type is a simple mechanical exercise which involves copying the structure of one of the mappers shown above and replacing the type (e.g. TraderId) with the new type (e.g CompanyId).

Conclusion


So that's it.

The application code is using the more expressive types and the Slick metadata is being manipulated to ensure that the use of the expressive types begins down at the level of the database metadata.

Refer to the code pulled from GitHub to see the complete example and to see some database reads and writes which use these id types. See the project README for the steps to actually run the code.