14 KiB
14 KiB
Core Package Specification
Overview
The Core package provides the foundation and entry point for our framework. It manages the application lifecycle, bootstraps services, handles HTTP requests, and coordinates all other framework packages.
Related Documentation
- See Laravel Compatibility Roadmap for implementation status
- See Foundation Integration Guide for integration patterns
- See Testing Guide for testing approaches
- See Getting Started Guide for development setup
- See Container Package Specification for dependency injection
- See Events Package Specification for application events
Core Features
1. Application
/// Core application class
class Application {
/// Container instance
final Container _container;
/// Service providers
final List<ServiceProvider> _providers = [];
/// Booted flag
bool _booted = false;
/// Environment
late final String environment;
/// Base path
late final String basePath;
Application(this._container) {
_container.instance<Application>(this);
_registerBaseBindings();
_registerCoreProviders();
}
/// Registers base bindings
void _registerBaseBindings() {
_container.instance<Container>(_container);
_container.instance<String>('base_path', basePath);
_container.instance<String>('env', environment);
}
/// Registers core providers
void _registerCoreProviders() {
register(EventServiceProvider());
register(LogServiceProvider());
register(RoutingServiceProvider());
register(ConfigServiceProvider());
}
/// Registers a service provider
void register(ServiceProvider provider) {
provider.app = this;
provider.register();
_providers.add(provider);
if (_booted) {
_bootProvider(provider);
}
}
/// Boots the application
Future<void> boot() async {
if (_booted) return;
for (var provider in _providers) {
await _bootProvider(provider);
}
_booted = true;
}
/// Boots a provider
Future<void> _bootProvider(ServiceProvider provider) async {
await provider.callBootingCallbacks();
await provider.boot();
await provider.callBootedCallbacks();
}
/// Handles HTTP request
Future<Response> handle(Request request) async {
try {
return await _pipeline.handle(request);
} catch (e) {
return _handleError(e, request);
}
}
/// Gets container instance
Container get container => _container;
/// Makes instance from container
T make<T>([dynamic parameters]) {
return _container.make<T>(parameters);
}
/// Gets environment
bool environment(String env) {
return this.environment == env;
}
/// Determines if application is in production
bool get isProduction => environment == 'production';
/// Determines if application is in development
bool get isDevelopment => environment == 'development';
/// Determines if application is in testing
bool get isTesting => environment == 'testing';
/// Gets base path
String path([String? path]) {
return [basePath, path].where((p) => p != null).join('/');
}
}
2. Service Providers
/// Base service provider
abstract class ServiceProvider {
/// Application instance
late Application app;
/// Container instance
Container get container => app.container;
/// Booting callbacks
final List<Function> _bootingCallbacks = [];
/// Booted callbacks
final List<Function> _bootedCallbacks = [];
/// Registers services
void register();
/// Boots services
Future<void> boot() async {}
/// Registers booting callback
void booting(Function callback) {
_bootingCallbacks.add(callback);
}
/// Registers booted callback
void booted(Function callback) {
_bootedCallbacks.add(callback);
}
/// Calls booting callbacks
Future<void> callBootingCallbacks() async {
for (var callback in _bootingCallbacks) {
await callback(app);
}
}
/// Calls booted callbacks
Future<void> callBootedCallbacks() async {
for (var callback in _bootedCallbacks) {
await callback(app);
}
}
}
/// Event service provider
class EventServiceProvider extends ServiceProvider {
@override
void register() {
container.singleton<EventDispatcherContract>((c) =>
EventDispatcher(c)
);
}
}
/// Routing service provider
class RoutingServiceProvider extends ServiceProvider {
@override
void register() {
container.singleton<RouterContract>((c) =>
Router(c)
);
}
@override
Future<void> boot() async {
var router = container.make<RouterContract>();
await loadRoutes(router);
}
}
3. HTTP Kernel
/// HTTP kernel
class HttpKernel {
/// Application instance
final Application _app;
/// Global middleware
final List<dynamic> middleware = [
CheckForMaintenanceMode::class,
ValidatePostSize::class,
TrimStrings::class,
ConvertEmptyStringsToNull::class
];
/// Route middleware groups
final Map<String, List<dynamic>> middlewareGroups = {
'web': [
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartSession::class,
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
SubstituteBindings::class
],
'api': [
'throttle:60,1',
SubstituteBindings::class
]
};
/// Route middleware aliases
final Map<String, dynamic> routeMiddleware = {
'auth': Authenticate::class,
'auth.basic': AuthenticateWithBasicAuth::class,
'bindings': SubstituteBindings::class,
'cache.headers': SetCacheHeaders::class,
'can': Authorize::class,
'guest': RedirectIfAuthenticated::class,
'signed': ValidateSignature::class,
'throttle': ThrottleRequests::class,
'verified': EnsureEmailIsVerified::class,
};
HttpKernel(this._app);
/// Handles HTTP request
Future<Response> handle(Request request) async {
try {
request = await _handleGlobalMiddleware(request);
return await _app.handle(request);
} catch (e) {
return _handleError(e, request);
}
}
/// Handles global middleware
Future<Request> _handleGlobalMiddleware(Request request) async {
var pipeline = _app.make<Pipeline>();
return await pipeline
.send(request)
.through(middleware)
.then((request) => request);
}
/// Handles error
Response _handleError(Object error, Request request) {
var handler = _app.make<ExceptionHandler>();
return handler.render(error, request);
}
}
4. Console Kernel
/// Console kernel
class ConsoleKernel {
/// Application instance
final Application _app;
/// Console commands
final List<dynamic> commands = [
// Framework Commands
KeyGenerateCommand::class,
ConfigCacheCommand::class,
ConfigClearCommand::class,
RouteListCommand::class,
RouteCacheCommand::class,
RouteClearCommand::class,
// App Commands
SendEmailsCommand::class,
PruneOldRecordsCommand::class
];
/// Command schedules
final Map<String, String> schedules = {
'emails:send': '0 * * * *',
'records:prune': '0 0 * * *'
};
ConsoleKernel(this._app);
/// Handles console command
Future<int> handle(List<String> args) async {
try {
var status = await _runCommand(args);
return status ?? 0;
} catch (e) {
_handleError(e);
return 1;
}
}
/// Runs console command
Future<int?> _runCommand(List<String> args) async {
var command = _resolveCommand(args);
if (command == null) return null;
return await command.run(args);
}
/// Resolves command from arguments
Command? _resolveCommand(List<String> args) {
if (args.isEmpty) return null;
var name = args.first;
var command = commands.firstWhere(
(c) => c.name == name,
orElse: () => null
);
if (command == null) return null;
return _app.make<Command>(command);
}
/// Handles error
void _handleError(Object error) {
stderr.writeln(error);
}
}
5. Exception Handler
/// Exception handler
class ExceptionHandler {
/// Application instance
final Application _app;
/// Exception renderers
final Map<Type, Function> _renderers = {
ValidationException: _renderValidationException,
AuthenticationException: _renderAuthenticationException,
AuthorizationException: _renderAuthorizationException,
NotFoundException: _renderNotFoundException,
HttpException: _renderHttpException
};
ExceptionHandler(this._app);
/// Renders exception to response
Response render(Object error, Request request) {
var renderer = _renderers[error.runtimeType];
if (renderer != null) {
return renderer(error, request);
}
return _renderGenericException(error, request);
}
/// Renders validation exception
Response _renderValidationException(
ValidationException e,
Request request
) {
if (request.wantsJson) {
return Response.json({
'message': 'The given data was invalid.',
'errors': e.errors
}, 422);
}
return Response.redirect()
.back()
.withErrors(e.errors)
.withInput(request.all());
}
/// Renders generic exception
Response _renderGenericException(Object e, Request request) {
if (_app.isProduction) {
return Response('Server Error', 500);
}
return Response(e.toString(), 500);
}
}
Integration Examples
1. Application Bootstrap
void main() async {
var container = Container();
var app = Application(container)
..environment = 'production'
..basePath = Directory.current.path;
await app.boot();
var server = HttpServer(app);
await server.start();
}
2. Service Provider
class AppServiceProvider extends ServiceProvider {
@override
void register() {
container.singleton<UserRepository>((c) =>
DatabaseUserRepository(c.make<Database>())
);
}
@override
Future<void> boot() async {
var config = container.make<ConfigContract>();
TimeZone.setDefault(config.get('app.timezone'));
}
}
3. HTTP Request Handling
class Server {
final HttpKernel kernel;
Future<void> handle(HttpRequest request) async {
var protevusRequest = await Request.fromHttpRequest(request);
var response = await kernel.handle(protevusRequest);
await response.send(request.response);
}
}
Testing
void main() {
group('Application', () {
test('boots providers', () async {
var app = Application(Container());
var provider = TestProvider();
app.register(provider);
await app.boot();
expect(provider.booted, isTrue);
});
test('handles requests', () async {
var app = Application(Container());
await app.boot();
var request = Request('GET', '/');
var response = await app.handle(request);
expect(response.statusCode, equals(200));
});
});
group('Service Provider', () {
test('registers services', () async {
var app = Application(Container());
var provider = TestProvider();
app.register(provider);
expect(app.make<TestService>(), isNotNull);
});
});
}
Next Steps
- Implement core application
- Add service providers
- Add HTTP kernel
- Add console kernel
- Write tests
- Add benchmarks
Development Guidelines
1. Getting Started
Before implementing core features:
- Review Getting Started Guide
- Check Laravel Compatibility Roadmap
- Follow Testing Guide
- Use Foundation Integration Guide
- Review Container Package Specification
- Review Events Package Specification
2. Implementation Process
For each core feature:
- Write tests following Testing Guide
- Implement following framework patterns
- Document following Getting Started Guide
- Integrate following Foundation Integration Guide
3. Quality Requirements
All implementations must:
- Pass all tests (see Testing Guide)
- Follow framework patterns
- Follow integration patterns (see Foundation Integration Guide)
- Support dependency injection (see Container Package Specification)
- Support event system (see Events Package Specification)
4. Integration Considerations
When implementing core features:
- Follow patterns in Foundation Integration Guide
- Use framework patterns consistently
- Use testing approaches from Testing Guide
- Follow development setup in Getting Started Guide
5. Performance Guidelines
Core system must:
- Boot efficiently
- Handle requests quickly
- Manage memory usage
- Scale horizontally
- Meet performance targets in Laravel Compatibility Roadmap
6. Testing Requirements
Core tests must:
- Cover all core features
- Test application lifecycle
- Verify service providers
- Check error handling
- Follow patterns in Testing Guide
7. Documentation Requirements
Core documentation must:
- Explain framework patterns
- Show lifecycle examples
- Cover error handling
- Include performance tips
- Follow standards in Getting Started Guide