Database setup for the Website service (#13)
This PR contains the work done to introduce a _Fluent_-based persistence layer for the Website service, selectable at runtime alongside the existing in-memory default, plus the local dev tooling and docs to support it. To provide further details about the work: * Persistence package * The `Driver` and `TLS` enumerations * The `Configuration` type * The `Service` factory that builds the service * `PrepareDB` for migrations registration * The `Probe` for readiness checks. * App integration * Builds the driver, registers migrations, and attaches `Fluent` to the service lifecycle so it starts/stops with the HTTP server. * Migrate-on-boot is gated to the in-memory backend; MySQL/MariaDB is migrated out of band via --database-migrate so shared databases never race on startup. * The `ConfigReader+Properties` extension maps database.* config keys onto the driver. * Library * Added database configuration constants. * The `HealthController` controller gains a readiness probe: `GET /health/ready` checks whether the database is reachable, separate from the existing liveness check. * Others * Updated the `docker-compose` files to support a database service behind a database profile, and hardened for local development * New database targets on the `Makefile` file and overall documentation updated * Updated the `.env.local`, `Dockerfile`, and `README` files to document the persistence workflow, config keys, and local DB commands Reviewed-on: rock-n-code/loud-amsterdam#13 Co-authored-by: Javier Cicchelli <javier@rock-n-code.com> Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
import FluentMySQLDriver
|
||||
import FluentSQLiteDriver
|
||||
import HummingbirdFluent
|
||||
import Logging
|
||||
|
||||
/// A factory building the `Fluent` service the application persists through.
|
||||
///
|
||||
/// Built once around the driver the executable picks at startup and called as a function to produce
|
||||
/// the configured service: `let fluent = service()`.
|
||||
public struct Service: Sendable {
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
/// The persistence backend to register.
|
||||
private let driver: Driver
|
||||
|
||||
/// The logger the database emits through.
|
||||
private let logger: Logger
|
||||
|
||||
// MARK: Initializers
|
||||
|
||||
/// Creates a factory for a `Fluent` service backed by the given driver.
|
||||
/// - Parameters:
|
||||
/// - driver: the persistence backend to register.
|
||||
/// - logger: the logger the database emits through.
|
||||
public init(
|
||||
driver: Driver,
|
||||
logger: Logger
|
||||
) {
|
||||
self.driver = driver
|
||||
self.logger = logger
|
||||
}
|
||||
|
||||
// MARK: Methods
|
||||
|
||||
/// Builds a `Fluent` service configured for the driver.
|
||||
///
|
||||
/// The selected backend is registered as the *default* database, so repositories resolve it with a plain
|
||||
/// `fluent.db()` and stay agnostic of which driver is in use. The returned service is not yet running; add
|
||||
/// it to the application's service group (`app.addServices(_:)`) so it starts and shuts its connection pool
|
||||
/// down alongside the server.
|
||||
/// - Returns: the configured `Fluent` service, ready to be added to the service group.
|
||||
public func callAsFunction() -> Fluent {
|
||||
let fluent = Fluent(
|
||||
logger: logger
|
||||
)
|
||||
|
||||
switch driver {
|
||||
case .mysql(let configuration):
|
||||
fluent.databases.use(
|
||||
.mysql(
|
||||
configuration: .init(
|
||||
hostname: configuration.host,
|
||||
port: configuration.port,
|
||||
username: configuration.username,
|
||||
password: configuration.password,
|
||||
database: configuration.name,
|
||||
tlsConfiguration: configuration.tls.tlsConfiguration
|
||||
),
|
||||
maxConnectionsPerEventLoop: configuration.maxConnectionsPerEventLoop
|
||||
),
|
||||
as: .mysql,
|
||||
isDefault: true
|
||||
)
|
||||
case .inMemory:
|
||||
// A single connection keeps every query pointed at the same in-memory store,
|
||||
// rather than each pooled connection getting its own private database.
|
||||
fluent.databases.use(
|
||||
.sqlite(.memory, maxConnectionsPerEventLoop: 1),
|
||||
as: .sqlite,
|
||||
isDefault: true
|
||||
)
|
||||
}
|
||||
|
||||
return fluent
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user