Services
import 'package:gisila/gisila.dart';Service
Section titled “Service”Base class for per-request business logic. The framework creates a fresh instance per request (on first use), attaches the request’s RequestContext and caches it for the rest of that request.
abstract class Service { Service(); RequestContext get ctx; T db<T>(); T singleton<T extends Object>(); void attach(RequestContext context);}When to use: put business rules and data access in services and keep controllers thin. A service can read the principal, the database session and app-wide singletons without them being passed through every call. Use a singleton instead for stateless, shareable clients (HTTP clients, mailers), and a plain function for pure logic that needs no request state.
| Member | Description |
|---|---|
ctx |
The request context. Throws StateError if read before attach (only happens when you construct a service yourself and forget to attach it). |
db<T>() |
Same as ctx.db<T>(). |
singleton<T>() |
Same as ctx.singleton<T>(). |
attach(context) |
Wires the service to a context. The framework calls it; call it yourself in unit tests. |
To use a service, register a factory and declare a parameter of the service type on a controller method (or call ctx.service<T>()). Services can depend on other services through ctx.service<Other>(); each type is created once per request.
Example
class OrderService extends Service { Future<Order> place(OrderForm form) async { final userId = ctx.principal!.id; final payments = ctx.service<PaymentService>(); await payments.charge(userId, form.total.value!); return orders.insert(userId: userId, total: form.total.value!); }}
// main():app ..registerService<OrderService>(OrderService.new) ..registerService<PaymentService>(() => PaymentService(app.singleton<StripeClient>()));
// controller:@Post('/orders')@RequireAuth()Future<Order> place(OrderForm form, OrderService orders) => orders.place(form);Unit-testing a service without the HTTP pipeline:
final service = OrderService() ..attach(RequestContext( requestId: 'test', request: Request('POST', Uri.parse('http://localhost/orders')), principal: const Principal(id: 'u1'), ));See also: Services and dependency scopes.
ServiceFactory
Section titled “ServiceFactory”typedef ServiceFactory<S extends Service> = S Function();The factory passed to GisilaApp.registerService: any zero-argument constructor tear-off (UserService.new) or closure. The closure runs per request, after init(), so it may read factory singletons with app.singleton<T>().