diff --git a/app/src/main/kotlin/net/h34t/filemure/TemplateModifiers.kt b/app/src/main/kotlin/net/h34t/filemure/TemplateModifiers.kt
index 3ac608c..837980a 100644
--- a/app/src/main/kotlin/net/h34t/filemure/TemplateModifiers.kt
+++ b/app/src/main/kotlin/net/h34t/filemure/TemplateModifiers.kt
@@ -5,7 +5,8 @@ import org.apache.commons.text.StringEscapeUtils
 import java.net.URLEncoder
 
 class TemplateModifiers : Frame.Modifiers, Limbo.Modifiers, DocumentCreateForm.Modifiers, Overview.Modifiers,
-    Document.Modifiers, FilePreview.Modifiers, DocumentEditForm.Modifiers, FileList.Modifiers {
+    FilePreview.Modifiers, DocumentEditForm.Modifiers, FileList.Modifiers,
+    net.h34t.filemure.tpl.Document.Modifiers {
 
     fun hashPrefix(arg: String): String {
         return URLEncoder.encode(arg, Charsets.UTF_8)
diff --git a/core/src/main/kotlin/net/h34t/filemure/core/entity/Types.kt b/app/src/main/kotlin/net/h34t/filemure/Types.kt
similarity index 82%
rename from core/src/main/kotlin/net/h34t/filemure/core/entity/Types.kt
rename to app/src/main/kotlin/net/h34t/filemure/Types.kt
index 22c36b5..cb38101 100644
--- a/core/src/main/kotlin/net/h34t/filemure/core/entity/Types.kt
+++ b/app/src/main/kotlin/net/h34t/filemure/Types.kt
@@ -1,10 +1,26 @@
-package net.h34t.filemure.core.entity
+package net.h34t.filemure
 
 import java.time.LocalDateTime
 
+
+@JvmInline
+value class ExtId(val value: String) {
+
+    companion object {
+        private val chars = ('a'..'z') + ('A'..'Z') + ('0'..'9')
+
+        fun generate(): ExtId {
+            return ExtId((0..8).map { chars.random() }.joinToString(""))
+        }
+    }
+
+    override fun toString() = value
+
+}
+
 data class Document(
     val id: Long,
-    val extId: String,
+    val extId: ExtId,
     val title: String,
     val description: String,
     val tags: List<Tag>,
@@ -20,19 +36,11 @@ value class Tag(val value: String) {
         // TODO proper validation
         require(value.isNotBlank())
     }
-
-    companion object {
-        fun parse(ser: String?): List<Tag> {
-            return ser?.let { if (it.isNotBlank()) it.split(",").map { Tag(it) } else emptyList() } ?: emptyList()
-        }
-
-        fun List<Tag>.serialize() = if (this.isEmpty()) "" else this.joinToString(",") { it.value }
-    }
 }
 
 data class FileRef(
     val id: Long,
-    val extId: String,
+    val extId: ExtId,
     val accountId: Long,
     val documentId: Long?,
     val filename: String,
@@ -45,7 +53,7 @@ data class FileRef(
 
 data class FileContent(
     val id: Long,
-    val extId: String,
+    val extId: ExtId,
     val filename: String,
     val contentType: String?,
     val contentExtracted: String?,
diff --git a/app/src/main/kotlin/net/h34t/filemure/Util.kt b/app/src/main/kotlin/net/h34t/filemure/Util.kt
index 0d5c10e..2659289 100644
--- a/app/src/main/kotlin/net/h34t/filemure/Util.kt
+++ b/app/src/main/kotlin/net/h34t/filemure/Util.kt
@@ -36,20 +36,6 @@ fun Context.setSession(session: Session?) = this.sessionAttribute("session", ses
 
 fun Context.requireSession(): Session = this.getSession() ?: throw UnauthorizedResponse("Not logged in")
 
-private val chars = ('a'..'z') + ('A'..'Z') + ('0'..'9')
-
-
-@JvmInline
-value class ExtId(val value: String) {
-    override fun toString() = value
-
-    companion object {
-        fun generate(): ExtId {
-            return ExtId((0..8).map { chars.random() }.joinToString(""))
-        }
-    }
-}
-
 fun formatHumanReadableSize(bytes: Long) = when (bytes) {
     in 0L..<1024L -> "$bytes bytes"
     in 1025..<1024 * 1_000 -> "${bytes / 1000} kb"
@@ -57,3 +43,10 @@ fun formatHumanReadableSize(bytes: Long) = when (bytes) {
     else -> "${bytes / 1_000_000_000} gb"
 }
 
+object TagAdapter {
+    fun parse(ser: String?): List<Tag> {
+        return ser?.let { if (it.isNotBlank()) it.split(",").map { Tag(it) } else emptyList() } ?: emptyList()
+    }
+
+    fun List<Tag>.serialize() = if (this.isEmpty()) "" else this.joinToString(",") { it.value }
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/net/h34t/filemure/controller/DocumentController.kt b/app/src/main/kotlin/net/h34t/filemure/controller/DocumentController.kt
index a27c05b..49a9191 100644
--- a/app/src/main/kotlin/net/h34t/filemure/controller/DocumentController.kt
+++ b/app/src/main/kotlin/net/h34t/filemure/controller/DocumentController.kt
@@ -5,10 +5,9 @@ import io.javalin.http.Context
 import io.javalin.http.ForbiddenResponse
 import io.javalin.http.Header
 import net.h34t.filemure.*
-import net.h34t.filemure.core.entity.State
-import net.h34t.filemure.core.entity.Tag
 import net.h34t.filemure.repository.SqliteRepository
 import net.h34t.filemure.tpl.*
+import net.h34t.filemure.tpl.Document
 import java.io.File
 import java.time.LocalDateTime
 import java.time.format.DateTimeFormatter
@@ -32,7 +31,7 @@ class DocumentController(val modifiers: TemplateModifiers, val repository: Sqlit
                 isTarget = true,
                 content = Document(
                     modifiers = modifiers,
-                    extId = document.extId,
+                    extId = document.extId.value,
                     title = document.title,
                     referenceDate = dtf.format(document.referenceDate),
                     tags = { document.tags.map { TagsBlock(tag = it.value) }.asSequence() },
@@ -43,7 +42,7 @@ class DocumentController(val modifiers: TemplateModifiers, val repository: Sqlit
                         files = {
                             document.files.map { file ->
                                 FilesBlock(
-                                    extId = file.extId,
+                                    extId = file.extId.value,
                                     filename = file.filename,
                                     contentType = file.contentType ?: "?",
                                     size = formatHumanReadableSize(file.fileSize),
@@ -60,7 +59,7 @@ class DocumentController(val modifiers: TemplateModifiers, val repository: Sqlit
     fun createDocumentForm(ctx: Context) {
         val session = ctx.requireSession()
 
-        val fileIds = ctx.queryParams("file_id")
+        val fileIds = ctx.queryParams("file_id").map { ExtId(it) }
 
         val limboFiles = repository.getFilesInLimbo(session.id)
         val limboFileIds = limboFiles.map { it.extId }
@@ -96,7 +95,7 @@ class DocumentController(val modifiers: TemplateModifiers, val repository: Sqlit
                         files = {
                             selectedFiles.map { file ->
                                 FilesBlock(
-                                    extId = file.extId,
+                                    extId = file.extId.value,
                                     filename = file.filename,
                                     contentType = file.contentType ?: "?",
                                     size = formatHumanReadableSize(file.fileSize),
@@ -174,7 +173,7 @@ class DocumentController(val modifiers: TemplateModifiers, val repository: Sqlit
                         files = {
                             document.files.map { file ->
                                 FilesBlock(
-                                    extId = file.extId,
+                                    extId = file.extId.value,
                                     filename = file.filename,
                                     contentType = file.contentType ?: "?",
                                     size = formatHumanReadableSize(file.fileSize),
@@ -207,7 +206,7 @@ class DocumentController(val modifiers: TemplateModifiers, val repository: Sqlit
             id = document.id,
             title = title ?: "",
             referenceDate = LocalDateTime.parse(referenceDate ?: "", formDtf),
-            tags = Tag.parse(tags),
+            tags = TagAdapter.parse(tags),
             description = description
         )
 
diff --git a/app/src/main/kotlin/net/h34t/filemure/controller/LimboController.kt b/app/src/main/kotlin/net/h34t/filemure/controller/LimboController.kt
index 5ede23c..b835886 100644
--- a/app/src/main/kotlin/net/h34t/filemure/controller/LimboController.kt
+++ b/app/src/main/kotlin/net/h34t/filemure/controller/LimboController.kt
@@ -26,7 +26,7 @@ class LimboController(val modifiers: TemplateModifiers, val repository: SqliteRe
                     modifiers = modifiers, limboFileCount = files.size.toString(), file = {
                         files.map { f ->
                             FileBlock(
-                                extId = f.extId,
+                                extId = f.extId.value,
                                 file = f.filename,
                                 type = f.contentType ?: "",
                                 size = f.fileSize.toString(),
diff --git a/app/src/main/kotlin/net/h34t/filemure/controller/OverviewController.kt b/app/src/main/kotlin/net/h34t/filemure/controller/OverviewController.kt
index 076ebc7..3bb4c3f 100644
--- a/app/src/main/kotlin/net/h34t/filemure/controller/OverviewController.kt
+++ b/app/src/main/kotlin/net/h34t/filemure/controller/OverviewController.kt
@@ -12,7 +12,7 @@ import java.time.format.FormatStyle
 
 class OverviewController(val modifiers: TemplateModifiers, val repository: SqliteRepository) {
 
-    val dtf = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.SHORT)
+    private val htmlDtf = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.SHORT)
 
     fun overview(ctx: Context) {
         val session = ctx.requireSession()
@@ -24,7 +24,7 @@ class OverviewController(val modifiers: TemplateModifiers, val repository: Sqlit
         ctx.tempolin(
             Frame(
                 modifiers = modifiers,
-                title = "Filemure Overview",
+                title = "Overview",
                 isTarget = true,
                 content = Overview(
                     modifiers = modifiers,
@@ -32,8 +32,8 @@ class OverviewController(val modifiers: TemplateModifiers, val repository: Sqlit
                     document = {
                         documents.map { document ->
                             DocumentBlock(
-                                extId = document.extId,
-                                referenceDate = dtf.format(document.referenceDate),
+                                extId = document.extId.value,
+                                referenceDate = htmlDtf.format(document.referenceDate),
                                 title = document.title.ifBlank { "untitled" },
                             )
                         }.asSequence()
diff --git a/app/src/main/kotlin/net/h34t/filemure/repository/SqliteRepository.kt b/app/src/main/kotlin/net/h34t/filemure/repository/SqliteRepository.kt
index 8699817..ef2277a 100644
--- a/app/src/main/kotlin/net/h34t/filemure/repository/SqliteRepository.kt
+++ b/app/src/main/kotlin/net/h34t/filemure/repository/SqliteRepository.kt
@@ -1,34 +1,78 @@
 package net.h34t.filemure.repository
 
+import app.cash.sqldelight.ColumnAdapter
 import app.cash.sqldelight.db.SqlDriver
 import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver
-import net.h34t.filemure.ExtId
-import net.h34t.filemure.core.entity.*
-import net.h34t.filemure.core.entity.Tag.Companion.serialize
+import net.h34t.filemure.*
+import net.h34t.filemure.TagAdapter.serialize
 import net.h34t.filemure.db.Database
+import net.h34t.filemure.db.File_
 import java.io.InputStream
 import java.time.LocalDateTime
 import java.time.format.DateTimeFormatter
 
 class SqliteRepository(url: String) {
 
-    private val sqliteDtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
+    private companion object Adapters {
+        private val sqliteDtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
+
+        val localDateTimeAdapter = object : ColumnAdapter<LocalDateTime, String> {
+            override fun decode(databaseValue: String): LocalDateTime {
+                return LocalDateTime.parse(databaseValue, sqliteDtf)
+            }
+
+            override fun encode(value: LocalDateTime): String {
+                return value.format(sqliteDtf)
+            }
+        }
+
+        val stateAdapter = object : ColumnAdapter<State, Long> {
+            override fun decode(databaseValue: Long): State = State.fromCode(databaseValue.toInt())
+            override fun encode(value: State): Long = value.code.toLong()
+        }
+
+        val tagsAdapter = object : ColumnAdapter<List<Tag>, String> {
+            override fun decode(databaseValue: String): List<Tag> = TagAdapter.parse(databaseValue)
+            override fun encode(value: List<Tag>): String = value.serialize()
+        }
+
+        val extIdAdapter = object : ColumnAdapter<ExtId, String> {
+            override fun decode(databaseValue: String): ExtId = ExtId(databaseValue)
+            override fun encode(value: ExtId): String = value.value
+        }
+    }
 
-    // private val connection: Connection = DriverManager.getConnection(url)
     private val database: Database
 
     init {
-        val driver: SqlDriver = JdbcSqliteDriver("jdbc:sqlite:test.db")
-        Database.Schema.create(driver)
-        database = Database(driver)
-    }
+        val driver: SqlDriver = JdbcSqliteDriver(url)
 
-    private fun toLDT(value: String) = LocalDateTime.parse(value, sqliteDtf)
+        try {
+            Database.Schema.create(driver)
+        } catch (_: Exception) {
+        }
+
+        database = Database(
+            driver = driver,
+            documentAdapter = net.h34t.filemure.db.Document.Adapter(
+                tagsAdapter = tagsAdapter,
+                createdAdapter = localDateTimeAdapter,
+                reference_dateAdapter = localDateTimeAdapter,
+                stateAdapter = stateAdapter,
+                ext_idAdapter = extIdAdapter
+            ),
+            file_Adapter = File_.Adapter(
+                createdAdapter = localDateTimeAdapter,
+                stateAdapter = stateAdapter,
+                ext_idAdapter = extIdAdapter
+            )
+        )
+    }
 
     fun addFileToLimbo(accountId: Long, filename: String, contentType: String?, size: Long, content: InputStream) {
         database.databaseQueries.insertFileIntoLimbo(
             account_id = accountId,
-            ext_id = ExtId.generate().value,
+            ext_id = ExtId.generate(),
             filename = filename,
             content_type = contentType,
             file_size = size,
@@ -37,12 +81,12 @@ class SqliteRepository(url: String) {
     }
 
     fun getLimboFileCount(accountId: Long, state: State = State.ACTIVE): Long {
-        return database.databaseQueries.getLimboFileCount(account_id = accountId, state = state.code.toLong())
+        return database.databaseQueries.getLimboFileCount(account_id = accountId, state = state)
             .executeAsOne()
     }
 
     fun getFilesInLimbo(accountId: Long, state: State = State.ACTIVE): List<FileRef> {
-        return database.databaseQueries.getFilesInLimbo(account_id = accountId, state = state.code.toLong())
+        return database.databaseQueries.getFilesInLimbo(account_id = accountId, state = state)
             .executeAsList()
             .map {
                 FileRef(
@@ -54,8 +98,8 @@ class SqliteRepository(url: String) {
                     fileSize = it.file_size,
                     contentType = it.content_type,
                     contentExtracted = it.content_extracted,
-                    created = toLDT(it.created),
-                    state = State.fromCode(it.state.toInt())
+                    created = it.created,
+                    state = it.state
                 )
             }
     }
@@ -72,11 +116,11 @@ class SqliteRepository(url: String) {
         database.databaseQueries.transaction {
             database.databaseQueries.addDocument(
                 account_id = accountId,
-                ext_id = extId.value,
+                ext_id = extId,
                 title = title,
                 description = description,
-                tags = tags.serialize(),
-                reference_date = referenceDate.format(sqliteDtf)
+                tags = tags,
+                reference_date = referenceDate
             )
 
             val documentId = database.databaseQueries.getLastInsertRowId().executeAsOne()
@@ -84,7 +128,7 @@ class SqliteRepository(url: String) {
             database.databaseQueries.attachLimboFilesToDocument(
                 account_id = accountId,
                 document_id = documentId,
-                ext_id = fileExtIds.map { it.value }
+                ext_id = fileExtIds
             )
         }
 
@@ -92,7 +136,7 @@ class SqliteRepository(url: String) {
     }
 
     fun getDocuments(accountId: Long, state: State = State.ACTIVE): List<Document> {
-        return database.databaseQueries.getDocuments(account_id = accountId, state = state.code.toLong())
+        return database.databaseQueries.getDocuments(account_id = accountId, state = state)
             .executeAsList()
             .map {
                 Document(
@@ -100,10 +144,10 @@ class SqliteRepository(url: String) {
                     extId = it.ext_id,
                     title = it.title,
                     description = it.description,
-                    tags = Tag.parse(it.tags),
-                    created = toLDT(it.created),
-                    referenceDate = toLDT(it.reference_date),
-                    state = State.fromCode(it.state.toInt()),
+                    tags = it.tags,
+                    created = it.created,
+                    referenceDate = it.reference_date,
+                    state = it.state,
                     files = emptyList()
                 )
             }
@@ -121,10 +165,10 @@ class SqliteRepository(url: String) {
         database.databaseQueries.updateDocument(
             id = id,
             title = title,
-            reference_date = referenceDate.format(sqliteDtf),
-            tags = tags.serialize(),
+            reference_date = referenceDate,
+            tags = tags,
             description = description,
-            state = state.code.toLong(),
+            state = state,
             account_id = accountId,
         )
     }
@@ -132,18 +176,18 @@ class SqliteRepository(url: String) {
     fun getDocumentByExtId(accountId: Long, extId: ExtId, state: State): Document {
         return database.databaseQueries.getDocumentByExtId(
             account_id = accountId,
-            ext_id = extId.value,
-            state = state.code.toLong()
+            ext_id = extId,
+            state = state
         ).executeAsOne().let { d ->
             Document(
                 id = d.id,
                 extId = d.ext_id,
                 title = d.title,
                 description = d.description,
-                tags = Tag.parse(d.tags),
-                created = toLDT(d.created),
-                referenceDate = toLDT(d.reference_date),
-                state = State.fromCode(d.state.toInt()),
+                tags = d.tags,
+                created = d.created,
+                referenceDate = d.reference_date,
+                state = d.state,
                 files = database.databaseQueries.getFilesForDocument(
                     document_id = d.id,
                     account_id = accountId
@@ -157,8 +201,8 @@ class SqliteRepository(url: String) {
                         contentType = f.content_type,
                         contentExtracted = f.content_extracted,
                         fileSize = f.file_size,
-                        created = toLDT(f.created),
-                        state = State.fromCode(f.state.toInt())
+                        created = f.created,
+                        state = f.state
                     )
                 }
             )
@@ -169,7 +213,7 @@ class SqliteRepository(url: String) {
     fun loadFile(accountId: Long, extId: ExtId): FileContent {
         return database
             .databaseQueries
-            .getFile(account_id = accountId, ext_id = extId.value)
+            .getFile(account_id = accountId, ext_id = extId)
             .executeAsOne().let { f ->
                 FileContent(
                     id = f.id,
diff --git a/app/src/main/sqldelight/net/h34t/filemure/db/Database.sq b/app/src/main/sqldelight/net/h34t/filemure/db/Database.sq
index 5c626f5..ca70713 100644
--- a/app/src/main/sqldelight/net/h34t/filemure/db/Database.sq
+++ b/app/src/main/sqldelight/net/h34t/filemure/db/Database.sq
@@ -1,28 +1,36 @@
+import java.time.LocalDateTime;
+import kotlin.collections.List;
+import net.h34t.filemure.ExtId;
+import net.h34t.filemure.State;
+import net.h34t.filemure.Tag;
+
 -- account definition
 
 CREATE TABLE account (
 	id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
-	email TEXT NOT NULL,
+    ext_id TEXT AS ExtId NOT NULL,
+ 	email TEXT NOT NULL,
 	password TEXT NOT NULL,
-	created TEXT DEFAULT (CURRENT_TIMESTAMP) NOT NULL,
-	state INTEGER DEFAULT (1) NOT NULL
+	created TEXT AS LocalDateTime DEFAULT (CURRENT_TIMESTAMP) NOT NULL,
+	state INTEGER AS State DEFAULT (1) NOT NULL
 );
 
 CREATE INDEX account_state_IDX ON account (state);
 CREATE UNIQUE INDEX account_email_IDX ON account (email);
+CREATE UNIQUE INDEX account_extid_IDX ON account (ext_id);
 
 -- document definition
 
 CREATE TABLE document (
 	id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
 	account_id INTEGER NOT NULL,
-	ext_id TEXT NOT NULL,
+	ext_id TEXT AS ExtId NOT NULL,
 	title TEXT NOT NULL,
 	description TEXT NOT NULL,
-	tags TEXT NOT NULL,
-	created TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
-	reference_date TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
-	state INTEGER NOT NULL DEFAULT (1),
+	tags TEXT AS List<Tag> NOT NULL,
+	created TEXT AS LocalDateTime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+	reference_date TEXT AS LocalDateTime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+	state INTEGER AS State NOT NULL DEFAULT (1),
   	CONSTRAINT document_account_FK FOREIGN KEY (account_id) REFERENCES account(id) ON DELETE CASCADE
 );
 
@@ -36,14 +44,14 @@ CREATE TABLE file (
 	id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
 	account_id INTEGER NOT NULL,
 	document_id INTEGER DEFAULT NULL,
-	ext_id TEXT NOT NULL,
+	ext_id TEXT AS ExtId NOT NULL,
 	filename TEXT NOT NULL,
 	file_size INTEGER NOT NULL,
 	content BLOB NOT NULL,
 	content_type TEXT,
 	content_extracted TEXT,
-	created TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
-	state INTEGER NOT NULL DEFAULT (1),
+	created TEXT AS LocalDateTime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+	state INTEGER AS State NOT NULL DEFAULT (1),
   	CONSTRAINT file_account_FK FOREIGN KEY (account_id) REFERENCES account(id) ON DELETE CASCADE,
 	CONSTRAINT file_document_FK FOREIGN KEY (document_id) REFERENCES document(id) ON DELETE CASCADE
 );