Skip to main content

Module Ecosystem and Extensions

The Hypermodern ecosystem includes a rich collection of community-contributed modules that extend the platform's capabilities. These modules provide pre-built solutions for common use cases and integrate seamlessly with the core platform.

Authentication Modules

OAuth2 Provider Module

// hypermodern_oauth2/lib/oauth2_module.dart
class OAuth2Module extends HypermodernModule {
  @override
  ModuleManifest get manifest => ModuleManifest.fromJson({
    'name': 'hypermodern_oauth2',
    'version': '2.1.0',
    'description': 'OAuth2 authentication provider with multiple grant types',
    'author': 'Hypermodern Community',
    'dependencies': {
      'hypermodern_auth': '^1.0.0',
      'hypermodern_crypto': '^1.0.0',
    },
    'exports': {
      'models': ['OAuthClient', 'AccessToken', 'RefreshToken', 'AuthorizationCode'],
      'endpoints': ['authorize', 'token', 'revoke', 'introspect'],
      'services': ['OAuth2Service', 'ClientService', 'TokenService'],
    },
    'configuration': {
      'authorization_code_ttl': {
        'type': 'duration',
        'default': '10m',
        'description': 'Authorization code time-to-live'
      },
      'access_token_ttl': {
        'type': 'duration',
        'default': '1h',
        'description': 'Access token time-to-live'
      },
      'refresh_token_ttl': {
        'type': 'duration',
        'default': '30d',
        'description': 'Refresh token time-to-live'
      },
      'supported_grant_types': {
        'type': 'array',
        'default': ['authorization_code', 'client_credentials', 'refresh_token'],
        'description': 'Supported OAuth2 grant types'
      }
    }
  });
  
  @override
  Future<void> register(ModuleContainer container) async {
    // Register services
    container.singleton<OAuth2Service>((c) => OAuth2Service(
      database: c.get<Database>(),
      config: c.config,
      cryptoService: c.get<CryptographyService>(),
    ));
    
    container.singleton<ClientService>((c) => ClientService(
      database: c.get<Database>(),
    ));
    
    container.singleton<TokenService>((c) => TokenService(
      database: c.get<Database>(),
      config: c.config,
    ));
  }
  
  @override
  Future<void> boot(ModuleContainer container) async {
    final server = container.get<HypermodernServer>();
    final oauth2Service = container.get<OAuth2Service>();
    
    // Authorization endpoint
    server.registerEndpoint<AuthorizeRequest, AuthorizeResponse>(
      'authorize',
      (request) async => await oauth2Service.authorize(request),
    );
    
    // Token endpoint
    server.registerEndpoint<TokenRequest, TokenResponse>(
      'token',
      (request) async => await oauth2Service.token(request),
    );
    
    // Token revocation
    server.registerEndpoint<RevokeRequest, RevokeResponse>(
      'revoke',
      (request) async => await oauth2Service.revoke(request),
    );
    
    // Token introspection
    server.registerEndpoint<IntrospectRequest, IntrospectResponse>(
      'introspect',
      (request) async => await oauth2Service.introspect(request),
    );
  }
  
  @override
  List<Migration> get migrations => [
    CreateOAuthClientsTableMigration(),
    CreateAccessTokensTableMigration(),
    CreateRefreshTokensTableMigration(),
    CreateAuthorizationCodesTableMigration(),
  ];
}

class OAuth2Service {
  final Database _db;
  final ModuleConfiguration _config;
  final CryptographyService _crypto;
  
  OAuth2Service({
    required Database database,
    required ModuleConfiguration config,
    required CryptographyService cryptoService,
  }) : _db = database,
       _config = config,
       _crypto = cryptoService;
  
  Future<AuthorizeResponse> authorize(AuthorizeRequest request) async {
    // Validate client
    final client = await _validateClient(request.clientId);
    
    // Validate redirect URI
    if (!client.redirectUris.contains(request.redirectUri)) {
      throw OAuth2Exception('invalid_request', 'Invalid redirect URI');
    }
    
    // Validate response type
    if (request.responseType != 'code') {
      throw OAuth2Exception('unsupported_response_type', 'Only authorization code flow supported');
    }
    
    // Generate authorization code
    final code = await _generateAuthorizationCode(
      clientId: request.clientId,
      userId: request.userId,
      scope: request.scope,
      redirectUri: request.redirectUri,
    );
    
    return AuthorizeResponse(
      code: code,
      state: request.state,
    );
  }
  
  Future<TokenResponse> token(TokenRequest request) async {
    switch (request.grantType) {
      case 'authorization_code':
        return await _handleAuthorizationCodeGrant(request);
      case 'client_credentials':
        return await _handleClientCredentialsGrant(request);
      case 'refresh_token':
        return await _handleRefreshTokenGrant(request);
      default:
        throw OAuth2Exception('unsupported_grant_type', 'Grant type not supported');
    }
  }
  
  Future<TokenResponse> _handleAuthorizationCodeGrant(TokenRequest request) async {
    // Validate authorization code
    final codeData = await _validateAuthorizationCode(request.code!);
    
    // Validate client
    await _validateClient(request.clientId!, request.clientSecret);
    
    // Validate redirect URI
    if (request.redirectUri != codeData.redirectUri) {
      throw OAuth2Exception('invalid_grant', 'Redirect URI mismatch');
    }
    
    // Generate tokens
    final accessToken = await _generateAccessToken(
      clientId: request.clientId!,
      userId: codeData.userId,
      scope: codeData.scope,
    );
    
    final refreshToken = await _generateRefreshToken(
      clientId: request.clientId!,
      userId: codeData.userId,
      scope: codeData.scope,
    );
    
    // Revoke authorization code
    await _revokeAuthorizationCode(request.code!);
    
    return TokenResponse(
      accessToken: accessToken.token,
      tokenType: 'Bearer',
      expiresIn: _config.getDuration('access_token_ttl').inSeconds,
      refreshToken: refreshToken.token,
      scope: codeData.scope,
    );
  }
  
  Future<String> _generateAuthorizationCode({
    required String clientId,
    required int userId,
    required String scope,
    required String redirectUri,
  }) async {
    final code = _crypto.generateSecureToken(length: 32);
    final expiresAt = DateTime.now().add(_config.getDuration('authorization_code_ttl'));
    
    await _db.query('''
      INSERT INTO authorization_codes (code, client_id, user_id, scope, redirect_uri, expires_at, created_at)
      VALUES (?, ?, ?, ?, ?, ?, ?)
    ''', [code, clientId, userId, scope, redirectUri, expiresAt, DateTime.now()]);
    
    return code;
  }
  
  Future<AccessToken> _generateAccessToken({
    required String clientId,
    required int userId,
    required String scope,
  }) async {
    final token = _crypto.generateSecureToken(length: 64);
    final expiresAt = DateTime.now().add(_config.getDuration('access_token_ttl'));
    
    await _db.query('''
      INSERT INTO access_tokens (token, client_id, user_id, scope, expires_at, created_at)
      VALUES (?, ?, ?, ?, ?, ?)
    ''', [token, clientId, userId, scope, expiresAt, DateTime.now()]);
    
    return AccessToken(
      token: token,
      clientId: clientId,
      userId: userId,
      scope: scope,
      expiresAt: expiresAt,
    );
  }
}

SAML Authentication Module

class SAMLModule extends HypermodernModule {
  @override
  ModuleManifest get manifest => ModuleManifest.fromJson({
    'name': 'hypermodern_saml',
    'version': '1.0.0',
    'description': 'SAML 2.0 authentication integration',
    'dependencies': {
      'hypermodern_auth': '^1.0.0',
      'hypermodern_crypto': '^1.0.0',
    },
    'configuration': {
      'identity_providers': {
        'type': 'array',
        'required': true,
        'description': 'List of SAML identity providers'
      },
      'service_provider': {
        'type': 'object',
        'required': true,
        'properties': {
          'entity_id': {'type': 'string', 'required': true},
          'assertion_consumer_service_url': {'type': 'string', 'required': true},
          'certificate': {'type': 'string', 'required': true},
          'private_key': {'type': 'string', 'required': true}
        }
      }
    }
  });
  
  @override
  Future<void> boot(ModuleContainer container) async {
    final server = container.get<HypermodernServer>();
    final samlService = container.get<SAMLService>();
    
    // SAML SSO initiation
    server.registerEndpoint<SAMLLoginRequest, SAMLLoginResponse>(
      'saml_login',
      (request) async => await samlService.initiateLogin(request),
    );
    
    // SAML assertion consumer service
    server.registerEndpoint<SAMLAssertionRequest, SAMLAssertionResponse>(
      'saml_acs',
      (request) async => await samlService.consumeAssertion(request),
    );
    
    // SAML metadata endpoint
    server.registerEndpoint<SAMLMetadataRequest, SAMLMetadataResponse>(
      'saml_metadata',
      (request) async => await samlService.getMetadata(request),
    );
  }
}

Database Integration Modules

MongoDB Module

class MongoDBModule extends HypermodernModule {
  @override
  ModuleManifest get manifest => ModuleManifest.fromJson({
    'name': 'hypermodern_mongodb',
    'version': '1.5.0',
    'description': 'MongoDB integration with ODM capabilities',
    'configuration': {
      'connection_string': {
        'type': 'string',
        'required': true,
        'description': 'MongoDB connection string'
      },
      'database_name': {
        'type': 'string',
        'required': true,
        'description': 'Default database name'
      },
      'connection_pool_size': {
        'type': 'int32',
        'default': 10,
        'description': 'Maximum connection pool size'
      }
    }
  });
  
  @override
  Future<void> register(ModuleContainer container) async {
    // Register MongoDB client
    container.singleton<MongoClient>((c) => MongoClient(
      connectionString: c.config.getString('connection_string'),
      poolSize: c.config.getInt('connection_pool_size'),
    ));
    
    // Register database
    container.singleton<MongoDatabase>((c) {
      final client = c.get<MongoClient>();
      return client.database(c.config.getString('database_name'));
    });
    
    // Register ODM
    container.singleton<MongoODM>((c) => MongoODM(
      database: c.get<MongoDatabase>(),
    ));
  }
}

class MongoODM {
  final MongoDatabase _database;
  final Map<Type, MongoCollection> _collections = {};
  
  MongoODM({required MongoDatabase database}) : _database = database;
  
  MongoRepository<T> repository<T>() {
    final collection = _getCollection<T>();
    return MongoRepository<T>(collection);
  }
  
  MongoCollection _getCollection<T>() {
    return _collections.putIfAbsent(T, () {
      final collectionName = _getCollectionName<T>();
      return _database.collection(collectionName);
    });
  }
  
  String _getCollectionName<T>() {
    // Convert class name to snake_case collection name
    final className = T.toString();
    return className.replaceAllMapped(
      RegExp(r'[A-Z]'),
      (match) => '_${match.group(0)!.toLowerCase()}',
    ).substring(1);
  }
}

class MongoRepository<T> {
  final MongoCollection _collection;
  
  MongoRepository(this._collection);
  
  Future<T?> findById(String id) async {
    final doc = await _collection.findOne({'_id': ObjectId.fromHexString(id)});
    return doc != null ? _fromDocument<T>(doc) : null;
  }
  
  Future<List<T>> find(Map<String, dynamic> filter) async {
    final cursor = _collection.find(filter);
    final docs = await cursor.toList();
    return docs.map((doc) => _fromDocument<T>(doc)).toList();
  }
  
  Future<T> save(T entity) async {
    final doc = _toDocument(entity);
    
    if (doc.containsKey('_id')) {
      await _collection.replaceOne({'_id': doc['_id']}, doc);
    } else {
      final result = await _collection.insertOne(doc);
      doc['_id'] = result.insertedId;
    }
    
    return _fromDocument<T>(doc);
  }
  
  Future<void> delete(String id) async {
    await _collection.deleteOne({'_id': ObjectId.fromHexString(id)});
  }
  
  T _fromDocument<T>(Map<String, dynamic> doc) {
    // Convert MongoDB document to Dart object
    // This would use reflection or code generation
    return (T as dynamic).fromJson(doc);
  }
  
  Map<String, dynamic> _toDocument(T entity) {
    // Convert Dart object to MongoDB document
    return (entity as dynamic).toJson();
  }
}

Redis Module

class RedisModule extends HypermodernModule {
  @override
  ModuleManifest get manifest => ModuleManifest.fromJson({
    'name': 'hypermodern_redis',
    'version': '2.0.0',
    'description': 'Redis integration for caching and pub/sub',
    'configuration': {
      'connection_string': {
        'type': 'string',
        'default': 'redis://localhost:6379',
        'description': 'Redis connection string'
      },
      'key_prefix': {
        'type': 'string',
        'default': 'hypermodern:',
        'description': 'Key prefix for all Redis operations'
      }
    }
  });
  
  @override
  Future<void> register(ModuleContainer container) async {
    container.singleton<RedisClient>((c) => RedisClient(
      connectionString: c.config.getString('connection_string'),
      keyPrefix: c.config.getString('key_prefix'),
    ));
    
    container.singleton<RedisCache>((c) => RedisCache(
      client: c.get<RedisClient>(),
    ));
    
    container.singleton<RedisPubSub>((c) => RedisPubSub(
      client: c.get<RedisClient>(),
    ));
  }
}

class RedisCache implements CacheProvider {
  final RedisClient _client;
  
  RedisCache({required RedisClient client}) : _client = client;
  
  @override
  Future<T?> get<T>(String key, T Function(String) deserializer) async {
    final value = await _client.get(key);
    return value != null ? deserializer(value) : null;
  }
  
  @override
  Future<void> set<T>(String key, T value, {Duration? ttl}) async {
    final serialized = jsonEncode((value as dynamic).toJson());
    
    if (ttl != null) {
      await _client.setex(key, ttl.inSeconds, serialized);
    } else {
      await _client.set(key, serialized);
    }
  }
  
  @override
  Future<void> delete(String key) async {
    await _client.del([key]);
  }
  
  @override
  Future<void> clear() async {
    final keys = await _client.keys('*');
    if (keys.isNotEmpty) {
      await _client.del(keys);
    }
  }
}

class RedisPubSub {
  final RedisClient _client;
  final Map<String, StreamController<String>> _subscriptions = {};
  
  RedisPubSub({required RedisClient client}) : _client = client;
  
  Future<void> publish(String channel, String message) async {
    await _client.publish(channel, message);
  }
  
  Stream<String> subscribe(String channel) {
    if (_subscriptions.containsKey(channel)) {
      return _subscriptions[channel]!.stream;
    }
    
    final controller = StreamController<String>.broadcast();
    _subscriptions[channel] = controller;
    
    _client.subscribe([channel]).listen((message) {
      if (message.channel == channel) {
        controller.add(message.payload);
      }
    });
    
    return controller.stream;
  }
  
  Future<void> unsubscribe(String channel) async {
    final controller = _subscriptions.remove(channel);
    if (controller != null) {
      await controller.close();
      await _client.unsubscribe([channel]);
    }
  }
}

Third-Party Service Integration Modules

Stripe Payment Module

class StripeModule extends HypermodernModule {
  @override
  ModuleManifest get manifest => ModuleManifest.fromJson({
    'name': 'hypermodern_stripe',
    'version': '3.0.0',
    'description': 'Stripe payment processing integration',
    'configuration': {
      'secret_key': {
        'type': 'string',
        'required': true,
        'description': 'Stripe secret key'
      },
      'webhook_secret': {
        'type': 'string',
        'required': true,
        'description': 'Stripe webhook endpoint secret'
      },
      'default_currency': {
        'type': 'string',
        'default': 'usd',
        'description': 'Default currency for payments'
      }
    }
  });
  
  @override
  Future<void> register(ModuleContainer container) async {
    container.singleton<StripeClient>((c) => StripeClient(
      secretKey: c.config.getString('secret_key'),
    ));
    
    container.singleton<PaymentService>((c) => PaymentService(
      stripeClient: c.get<StripeClient>(),
      config: c.config,
    ));
    
    container.singleton<WebhookService>((c) => WebhookService(
      stripeClient: c.get<StripeClient>(),
      webhookSecret: c.config.getString('webhook_secret'),
    ));
  }
  
  @override
  Future<void> boot(ModuleContainer container) async {
    final server = container.get<HypermodernServer>();
    final paymentService = container.get<PaymentService>();
    final webhookService = container.get<WebhookService>();
    
    // Payment endpoints
    server.registerEndpoint<CreatePaymentIntentRequest, PaymentIntentResponse>(
      'create_payment_intent',
      (request) async => await paymentService.createPaymentIntent(request),
    );
    
    server.registerEndpoint<ConfirmPaymentRequest, PaymentResponse>(
      'confirm_payment',
      (request) async => await paymentService.confirmPayment(request),
    );
    
    // Webhook endpoint
    server.registerEndpoint<StripeWebhookRequest, WebhookResponse>(
      'stripe_webhook',
      (request) async => await webhookService.handleWebhook(request),
    );
  }
}

class PaymentService {
  final StripeClient _stripe;
  final ModuleConfiguration _config;
  
  PaymentService({
    required StripeClient stripeClient,
    required ModuleConfiguration config,
  }) : _stripe = stripeClient,
       _config = config;
  
  Future<PaymentIntentResponse> createPaymentIntent(CreatePaymentIntentRequest request) async {
    final paymentIntent = await _stripe.paymentIntents.create({
      'amount': request.amount,
      'currency': request.currency ?? _config.getString('default_currency'),
      'customer': request.customerId,
      'metadata': request.metadata,
      'automatic_payment_methods': {'enabled': true},
    });
    
    return PaymentIntentResponse(
      id: paymentIntent.id,
      clientSecret: paymentIntent.clientSecret,
      status: paymentIntent.status,
      amount: paymentIntent.amount,
      currency: paymentIntent.currency,
    );
  }
  
  Future<PaymentResponse> confirmPayment(ConfirmPaymentRequest request) async {
    final paymentIntent = await _stripe.paymentIntents.confirm(
      request.paymentIntentId,
      {
        'payment_method': request.paymentMethodId,
        'return_url': request.returnUrl,
      },
    );
    
    return PaymentResponse(
      id: paymentIntent.id,
      status: paymentIntent.status,
      amount: paymentIntent.amount,
      currency: paymentIntent.currency,
      nextAction: paymentIntent.nextAction,
    );
  }
}

SendGrid Email Module

class SendGridModule extends HypermodernModule {
  @override
  ModuleManifest get manifest => ModuleManifest.fromJson({
    'name': 'hypermodern_sendgrid',
    'version': '1.2.0',
    'description': 'SendGrid email service integration',
    'configuration': {
      'api_key': {
        'type': 'string',
        'required': true,
        'description': 'SendGrid API key'
      },
      'default_from_email': {
        'type': 'string',
        'required': true,
        'description': 'Default sender email address'
      },
      'default_from_name': {
        'type': 'string',
        'default': 'Hypermodern App',
        'description': 'Default sender name'
      }
    }
  });
  
  @override
  Future<void> register(ModuleContainer container) async {
    container.singleton<SendGridClient>((c) => SendGridClient(
      apiKey: c.config.getString('api_key'),
    ));
    
    container.singleton<EmailService>((c) => EmailService(
      sendGridClient: c.get<SendGridClient>(),
      config: c.config,
    ));
    
    container.singleton<TemplateService>((c) => TemplateService(
      sendGridClient: c.get<SendGridClient>(),
    ));
  }
  
  @override
  Future<void> boot(ModuleContainer container) async {
    final server = container.get<HypermodernServer>();
    final emailService = container.get<EmailService>();
    
    server.registerEndpoint<SendEmailRequest, SendEmailResponse>(
      'send_email',
      (request) async => await emailService.sendEmail(request),
    );
    
    server.registerEndpoint<SendTemplateEmailRequest, SendEmailResponse>(
      'send_template_email',
      (request) async => await emailService.sendTemplateEmail(request),
    );
  }
}

class EmailService {
  final SendGridClient _sendGrid;
  final ModuleConfiguration _config;
  
  EmailService({
    required SendGridClient sendGridClient,
    required ModuleConfiguration config,
  }) : _sendGrid = sendGridClient,
       _config = config;
  
  Future<SendEmailResponse> sendEmail(SendEmailRequest request) async {
    final email = Email(
      from: EmailAddress(
        email: request.fromEmail ?? _config.getString('default_from_email'),
        name: request.fromName ?? _config.getString('default_from_name'),
      ),
      to: request.to.map((addr) => EmailAddress(email: addr)).toList(),
      subject: request.subject,
      content: [
        if (request.textContent != null)
          Content(type: 'text/plain', value: request.textContent!),
        if (request.htmlContent != null)
          Content(type: 'text/html', value: request.htmlContent!),
      ],
      attachments: request.attachments?.map((att) => Attachment(
        content: att.content,
        filename: att.filename,
        type: att.contentType,
      )).toList(),
    );
    
    final response = await _sendGrid.send(email);
    
    return SendEmailResponse(
      messageId: response.messageId,
      status: response.statusCode == 202 ? 'sent' : 'failed',
    );
  }
  
  Future<SendEmailResponse> sendTemplateEmail(SendTemplateEmailRequest request) async {
    final email = TemplateEmail(
      from: EmailAddress(
        email: _config.getString('default_from_email'),
        name: _config.getString('default_from_name'),
      ),
      to: request.to.map((addr) => EmailAddress(email: addr)).toList(),
      templateId: request.templateId,
      dynamicTemplateData: request.templateData,
    );
    
    final response = await _sendGrid.sendTemplate(email);
    
    return SendEmailResponse(
      messageId: response.messageId,
      status: response.statusCode == 202 ? 'sent' : 'failed',
    );
  }
}

Creating Module Libraries

Module Development Best Practices

abstract class ModuleDevelopmentGuide {
  // 1. Module Structure
  static const String moduleStructure = '''
  my_module/
  ├── lib/
  │   ├── src/
  │   │   ├── models/
  │   │   ├── services/
  │   │   ├── providers/
  │   │   └── middleware/
  │   ├── my_module.dart          # Main export file
  │   └── module.dart             # Module definition
  ├── schemas/
  │   ├── models.json
  │   ├── endpoints.json
  │   └── enums.json
  ├── test/
  │   ├── unit/
  │   ├── integration/
  │   └── module_test.dart
  ├── example/
  │   └── main.dart
  ├── CHANGELOG.md
  ├── LICENSE
  ├── README.md
  ├── module.json                 # Module manifest
  └── pubspec.yaml
  ''';
  
  // 2. Naming Conventions
  static const Map<String, String> namingConventions = {
    'module_name': 'hypermodern_feature_name (snake_case)',
    'class_names': 'PascalCase',
    'method_names': 'camelCase',
    'constants': 'SCREAMING_SNAKE_CASE',
    'files': 'snake_case.dart',
  };
  
  // 3. Documentation Requirements
  static const List<String> documentationRequirements = [
    'Comprehensive README with usage examples',
    'API documentation for all public methods',
    'Configuration options documentation',
    'Migration guide for breaking changes',
    'Contributing guidelines',
  ];
  
  // 4. Testing Requirements
  static const List<String> testingRequirements = [
    'Unit tests for all services and utilities',
    'Integration tests for module functionality',
    'Example application demonstrating usage',
    'Performance benchmarks for critical paths',
    'Compatibility tests with different Hypermodern versions',
  ];
}

// Example module template
class ExampleModule extends HypermodernModule {
  @override
  ModuleManifest get manifest => ModuleManifest.fromJson({
    'name': 'hypermodern_example',
    'version': '1.0.0',
    'description': 'Example module demonstrating best practices',
    'author': 'Your Name <your.email@example.com>',
    'homepage': 'https://github.com/yourorg/hypermodern_example',
    'repository': 'https://github.com/yourorg/hypermodern_example.git',
    'license': 'MIT',
    
    // Dependencies
    'hypermodern_version': '>=1.0.0 <2.0.0',
    'dependencies': {
      'hypermodern_auth': '^1.0.0',
    },
    'dev_dependencies': {
      'hypermodern_testing': '^1.0.0',
    },
    
    // Exports
    'exports': {
      'models': ['ExampleModel'],
      'endpoints': ['example_endpoint'],
      'services': ['ExampleService'],
      'middleware': ['ExampleMiddleware'],
    },
    
    // Configuration schema
    'configuration': {
      'feature_enabled': {
        'type': 'bool',
        'default': true,
        'description': 'Enable the example feature',
      },
      'api_endpoint': {
        'type': 'string',
        'required': true,
        'description': 'External API endpoint URL',
      },
      'timeout': {
        'type': 'duration',
        'default': '30s',
        'description': 'Request timeout duration',
      },
    },
    
    // Permissions required by this module
    'permissions': [
      'example.read',
      'example.write',
    ],
    
    // Database tables created by this module
    'database_tables': [
      'example_data',
      'example_logs',
    ],
    
    // Module metadata
    'tags': ['example', 'template', 'best-practices'],
    'keywords': ['hypermodern', 'module', 'example'],
  });
  
  @override
  Future<void> register(ModuleContainer container) async {
    // Register services with dependency injection
    container.singleton<ExampleService>((c) => ExampleService(
      database: c.get<Database>(),
      config: c.config,
      httpClient: c.get<HttpClient>(),
    ));
    
    // Register middleware
    container.singleton<ExampleMiddleware>((c) => ExampleMiddleware(
      service: c.get<ExampleService>(),
    ));
    
    // Register event listeners
    container.singleton<ExampleEventListener>((c) => ExampleEventListener(
      service: c.get<ExampleService>(),
    ));
  }
  
  @override
  Future<void> boot(ModuleContainer container) async {
    final server = container.get<HypermodernServer>();
    final service = container.get<ExampleService>();
    final middleware = container.get<ExampleMiddleware>();
    
    // Register endpoints
    server.registerEndpoint<ExampleRequest, ExampleResponse>(
      'example_endpoint',
      (request) async => await service.handleRequest(request),
    );
    
    // Register middleware conditionally
    if (container.config.getBool('feature_enabled')) {
      server.middleware.add(middleware);
    }
    
    // Set up event listeners
    final eventListener = container.get<ExampleEventListener>();
    server.events.listen(eventListener.handleEvent);
  }
  
  @override
  List<Migration> get migrations => [
    CreateExampleDataTableMigration(),
    CreateExampleLogsTableMigration(),
    AddExampleIndexesMigration(),
  ];
  
  @override
  Future<void> onInstall(ModuleContext context) async {
    // Perform installation tasks
    await _createDefaultConfiguration(context);
    await _setupExternalIntegrations(context);
    
    print('✅ Example module installed successfully');
  }
  
  @override
  Future<void> onUninstall(ModuleContext context) async {
    // Cleanup tasks
    await _cleanupExternalIntegrations(context);
    await _archiveData(context);
    
    print('🗑️ Example module uninstalled');
  }
  
  @override
  Future<void> onUpdate(ModuleContext context, String fromVersion) async {
    // Handle version-specific updates
    if (_shouldMigrateFrom(fromVersion, '0.9.0')) {
      await _migrateFromV0_9_0(context);
    }
    
    print('🔄 Example module updated from $fromVersion to ${manifest.version}');
  }
  
  Future<void> _createDefaultConfiguration(ModuleContext context) async {
    // Create default configuration files
    final configPath = path.join(context.modulePath, 'config', 'default.yaml');
    await File(configPath).parent.create(recursive: true);
    await File(configPath).writeAsString('''
example:
  feature_enabled: true
  timeout: 30s
  log_level: info
''');
  }
  
  Future<void> _setupExternalIntegrations(ModuleContext context) async {
    // Set up external service integrations
    final apiEndpoint = context.configuration.getString('api_endpoint');
    
    // Validate external service connectivity
    final client = HttpClient();
    try {
      final response = await client.get('$apiEndpoint/health');
      if (response.statusCode != 200) {
        throw ModuleInstallationException('External service not available');
      }
    } catch (e) {
      throw ModuleInstallationException('Failed to connect to external service: $e');
    }
  }
  
  bool _shouldMigrateFrom(String fromVersion, String targetVersion) {
    // Version comparison logic
    return Version.parse(fromVersion) < Version.parse(targetVersion);
  }
}

Module Testing Framework

class ModuleTestFramework {
  final ModuleContainer _container;
  final TestDatabase _testDb;
  final MockHttpServer _mockServer;
  
  ModuleTestFramework({
    required ModuleContainer container,
    required TestDatabase testDb,
    required MockHttpServer mockServer,
  }) : _container = container,
       _testDb = testDb,
       _mockServer = mockServer;
  
  Future<void> testModule<T extends HypermodernModule>(T module) async {
    group('${module.manifest.name} Module Tests', () {
      setUpAll(() async {
        await _testDb.initialize();
        await _mockServer.start();
        await module.register(_container);
        await module.boot(_container);
      });
      
      tearDownAll(() async {
        await _testDb.cleanup();
        await _mockServer.stop();
      });
      
      group('Module Lifecycle', () {
        test('should register services correctly', () async {
          // Test service registration
          for (final service in module.manifest.exports['services'] ?? []) {
            expect(_container.has(service), isTrue, reason: 'Service $service not registered');
          }
        });
        
        test('should boot without errors', () async {
          // Module should boot successfully
          expect(() => module.boot(_container), returnsNormally);
        });
        
        test('should handle installation', () async {
          final context = MockModuleContext();
          await module.onInstall(context);
          
          // Verify installation side effects
          expect(context.installationCompleted, isTrue);
        });
      });
      
      group('Configuration', () {
        test('should validate configuration schema', () {
          final config = module.manifest.configuration;
          
          for (final entry in config.entries) {
            final field = entry.value;
            
            // Test required fields
            if (field['required'] == true) {
              expect(
                () => _container.config.get(entry.key),
                throwsA(isA<ConfigurationException>()),
                reason: 'Required field ${entry.key} should throw when missing',
              );
            }
            
            // Test default values
            if (field.containsKey('default')) {
              _container.config.set(entry.key, null);
              final defaultValue = _container.config.get(entry.key);
              expect(defaultValue, equals(field['default']));
            }
          }
        });
      });
      
      group('Database Integration', () {
        test('should create required tables', () async {
          final tables = module.manifest.databaseTables;
          
          for (final table in tables) {
            final exists = await _testDb.tableExists(table);
            expect(exists, isTrue, reason: 'Table $table should exist');
          }
        });
        
        test('should run migrations successfully', () async {
          for (final migration in module.migrations) {
            await migration.up(_testDb);
            
            // Verify migration effects
            expect(await _testDb.migrationExists(migration.id), isTrue);
          }
        });
      });
      
      group('API Endpoints', () {
        for (final endpoint in module.manifest.exports['endpoints'] ?? []) {
          test('should handle $endpoint requests', () async {
            // Test endpoint functionality
            await _testEndpoint(endpoint);
          });
        }
      });
      
      group('Error Handling', () {
        test('should handle invalid requests gracefully', () async {
          // Test error scenarios
          await _testErrorScenarios(module);
        });
      });
      
      group('Performance', () {
        test('should meet performance requirements', () async {
          await _testPerformance(module);
        });
      });
    });
  }
  
  Future<void> _testEndpoint(String endpoint) async {
    // Generate test request
    final request = _generateTestRequest(endpoint);
    
    // Execute endpoint
    final server = _container.get<HypermodernServer>();
    final response = await server.handleRequest(endpoint, request);
    
    // Validate response
    expect(response, isNotNull);
    expect(response.statusCode, equals(200));
  }
  
  Future<void> _testErrorScenarios(HypermodernModule module) async {
    // Test various error conditions
    final errorScenarios = [
      'invalid_request_data',
      'missing_authentication',
      'insufficient_permissions',
      'external_service_unavailable',
    ];
    
    for (final scenario in errorScenarios) {
      await _testErrorScenario(scenario);
    }
  }
  
  Future<void> _testPerformance(HypermodernModule module) async {
    // Performance benchmarks
    final benchmarks = {
      'endpoint_response_time': Duration(milliseconds: 100),
      'database_query_time': Duration(milliseconds: 50),
      'memory_usage': 50 * 1024 * 1024, // 50MB
    };
    
    for (final entry in benchmarks.entries) {
      final result = await _measurePerformance(entry.key);
      expect(result, lessThan(entry.value), reason: '${entry.key} performance requirement not met');
    }
  }
}

// Usage example
void main() {
  final testFramework = ModuleTestFramework(
    container: TestModuleContainer(),
    testDb: TestDatabase(),
    mockServer: MockHttpServer(),
  );
  
  testFramework.testModule(ExampleModule());
}

Module Versioning and Dependencies

Semantic Versioning for Modules

class ModuleVersionManager {
  static bool isCompatible(String moduleVersion, String requiredVersion) {
    final module = Version.parse(moduleVersion);
    final required = Version.parse(requiredVersion);
    
    // Major version must match
    if (module.major != required.major) {
      return false;
    }
    
    // Minor version must be >= required
    if (module.minor < required.minor) {
      return false;
    }
    
    // Patch version must be >= required if minor versions match
    if (module.minor == required.minor && module.patch < required.patch) {
      return false;
    }
    
    return true;
  }
  
  static List<String> getBreakingChanges(String fromVersion, String toVersion) {
    final from = Version.parse(fromVersion);
    final to = Version.parse(toVersion);
    
    final changes = <String>[];
    
    if (to.major > from.major) {
      changes.add('Major version upgrade - breaking changes expected');
    }
    
    if (to.minor > from.minor) {
      changes.add('Minor version upgrade - new features added');
    }
    
    if (to.patch > from.patch) {
      changes.add('Patch version upgrade - bug fixes');
    }
    
    return changes;
  }
}

class DependencyResolver {
  final ModuleRegistry _registry;
  
  DependencyResolver(this._registry);
  
  Future<List<ModuleDependency>> resolveDependencies(
    Map<String, String> dependencies,
  ) async {
    final resolved = <ModuleDependency>[];
    final visited = <String>{};
    
    for (final entry in dependencies.entries) {
      await _resolveDependency(
        entry.key,
        entry.value,
        resolved,
        visited,
      );
    }
    
    return _topologicalSort(resolved);
  }
  
  Future<void> _resolveDependency(
    String moduleName,
    String versionConstraint,
    List<ModuleDependency> resolved,
    Set<String> visited,
  ) async {
    if (visited.contains(moduleName)) {
      return; // Already processed
    }
    
    visited.add(moduleName);
    
    // Find compatible version
    final availableVersions = await _registry.getAvailableVersions(moduleName);
    final compatibleVersion = _findCompatibleVersion(availableVersions, versionConstraint);
    
    if (compatibleVersion == null) {
      throw DependencyResolutionException(
        'No compatible version found for $moduleName $versionConstraint',
      );
    }
    
    // Get module info
    final moduleInfo = await _registry.getModuleInfo(moduleName, compatibleVersion);
    
    // Resolve transitive dependencies
    for (final dep in moduleInfo.dependencies.entries) {
      await _resolveDependency(dep.key, dep.value, resolved, visited);
    }
    
    // Add to resolved list
    resolved.add(ModuleDependency(
      name: moduleName,
      version: compatibleVersion,
      dependencies: moduleInfo.dependencies,
    ));
  }
  
  String? _findCompatibleVersion(List<String> versions, String constraint) {
    // Parse constraint (e.g., "^1.0.0", ">=1.2.0 <2.0.0")
    final constraintParser = VersionConstraintParser();
    final parsedConstraint = constraintParser.parse(constraint);
    
    // Find highest compatible version
    final compatibleVersions = versions
        .map(Version.parse)
        .where(parsedConstraint.allows)
        .toList()
      ..sort((a, b) => b.compareTo(a)); // Descending order
    
    return compatibleVersions.isNotEmpty ? compatibleVersions.first.toString() : null;
  }
  
  List<ModuleDependency> _topologicalSort(List<ModuleDependency> dependencies) {
    final sorted = <ModuleDependency>[];
    final visited = <String>{};
    final visiting = <String>{};
    
    void visit(ModuleDependency dep) {
      if (visiting.contains(dep.name)) {
        throw DependencyResolutionException('Circular dependency detected: ${dep.name}');
      }
      
      if (visited.contains(dep.name)) {
        return;
      }
      
      visiting.add(dep.name);
      
      // Visit dependencies first
      for (final depName in dep.dependencies.keys) {
        final dependency = dependencies.firstWhere((d) => d.name == depName);
        visit(dependency);
      }
      
      visiting.remove(dep.name);
      visited.add(dep.name);
      sorted.add(dep);
    }
    
    for (final dep in dependencies) {
      visit(dep);
    }
    
    return sorted;
  }
}

Contributing to the Ecosystem

Module Contribution Guidelines

class ModuleContributionGuide {
  static const String contributionProcess = '''
  1. Module Proposal
     - Create RFC (Request for Comments) document
     - Discuss with community on GitHub Discussions
     - Get feedback from core maintainers
  
  2. Development
     - Follow module development best practices
     - Implement comprehensive tests
     - Write detailed documentation
     - Follow code style guidelines
  
  3. Review Process
     - Submit pull request to module registry
     - Code review by maintainers
     - Security audit for sensitive modules
     - Performance benchmarking
  
  4. Publication
     - Module published to registry
     - Documentation added to website
     - Announcement in community channels
     - Version tagged and released
  ''';
  
  static const List<String> qualityStandards = [
    'Comprehensive test coverage (>90%)',
    'Clear and detailed documentation',
    'Semantic versioning compliance',
    'Security best practices followed',
    'Performance benchmarks provided',
    'Backward compatibility maintained',
    'Error handling and logging implemented',
    'Configuration validation included',
  ];
  
  static const Map<String, String> moduleCategories = {
    'authentication': 'Authentication and authorization modules',
    'database': 'Database integration and ORM modules',
    'messaging': 'Message queues and pub/sub modules',
    'storage': 'File storage and CDN modules',
    'monitoring': 'Logging, metrics, and monitoring modules',
    'payment': 'Payment processing modules',
    'communication': 'Email, SMS, and notification modules',
    'integration': 'Third-party service integration modules',
    'utility': 'General utility and helper modules',
  };
}

class ModuleQualityChecker {
  static Future<QualityReport> checkModule(String modulePath) async {
    final report = QualityReport();
    
    // Check manifest
    await _checkManifest(modulePath, report);
    
    // Check code quality
    await _checkCodeQuality(modulePath, report);
    
    // Check tests
    await _checkTests(modulePath, report);
    
    // Check documentation
    await _checkDocumentation(modulePath, report);
    
    // Check security
    await _checkSecurity(modulePath, report);
    
    return report;
  }
  
  static Future<void> _checkManifest(String modulePath, QualityReport report) async {
    final manifestFile = File(path.join(modulePath, 'module.json'));
    
    if (!await manifestFile.exists()) {
      report.addError('Module manifest (module.json) is missing');
      return;
    }
    
    try {
      final manifestContent = await manifestFile.readAsString();
      final manifest = jsonDecode(manifestContent) as Map<String, dynamic>;
      
      // Check required fields
      final requiredFields = ['name', 'version', 'description', 'author'];
      for (final field in requiredFields) {
        if (!manifest.containsKey(field)) {
          report.addError('Required manifest field missing: $field');
        }
      }
      
      // Validate version format
      try {
        Version.parse(manifest['version'] as String);
      } catch (e) {
        report.addError('Invalid version format: ${manifest['version']}');
      }
      
      // Check configuration schema
      if (manifest.containsKey('configuration')) {
        _validateConfigurationSchema(manifest['configuration'], report);
      }
      
    } catch (e) {
      report.addError('Invalid manifest JSON: $e');
    }
  }
  
  static Future<void> _checkCodeQuality(String modulePath, QualityReport report) async {
    // Run dart analyze
    final analyzeResult = await Process.run('dart', ['analyze', modulePath]);
    
    if (analyzeResult.exitCode != 0) {
      report.addWarning('Code analysis issues found:\n${analyzeResult.stdout}');
    }
    
    // Check for TODO/FIXME comments
    final dartFiles = await _findDartFiles(modulePath);
    for (final file in dartFiles) {
      final content = await File(file).readAsString();
      
      if (content.contains('TODO') || content.contains('FIXME')) {
        report.addWarning('TODO/FIXME comments found in $file');
      }
    }
  }
  
  static Future<void> _checkTests(String modulePath, QualityReport report) async {
    final testDir = Directory(path.join(modulePath, 'test'));
    
    if (!await testDir.exists()) {
      report.addError('Test directory is missing');
      return;
    }
    
    // Run tests
    final testResult = await Process.run('dart', ['test', modulePath]);
    
    if (testResult.exitCode != 0) {
      report.addError('Tests are failing:\n${testResult.stdout}');
    }
    
    // Check test coverage
    final coverageResult = await Process.run('dart', [
      'test',
      '--coverage=coverage',
      modulePath,
    ]);
    
    if (coverageResult.exitCode == 0) {
      final coverage = await _calculateCoverage(path.join(modulePath, 'coverage'));
      
      if (coverage < 90.0) {
        report.addWarning('Test coverage is below 90%: ${coverage.toStringAsFixed(1)}%');
      }
    }
  }
  
  static Future<void> _checkDocumentation(String modulePath, QualityReport report) async {
    // Check README
    final readmeFile = File(path.join(modulePath, 'README.md'));
    if (!await readmeFile.exists()) {
      report.addError('README.md is missing');
    } else {
      final content = await readmeFile.readAsString();
      
      if (content.length < 500) {
        report.addWarning('README.md is too short (less than 500 characters)');
      }
      
      // Check for required sections
      final requiredSections = ['Installation', 'Usage', 'Configuration'];
      for (final section in requiredSections) {
        if (!content.toLowerCase().contains(section.toLowerCase())) {
          report.addWarning('README.md missing section: $section');
        }
      }
    }
    
    // Check CHANGELOG
    final changelogFile = File(path.join(modulePath, 'CHANGELOG.md'));
    if (!await changelogFile.exists()) {
      report.addWarning('CHANGELOG.md is missing');
    }
    
    // Check LICENSE
    final licenseFile = File(path.join(modulePath, 'LICENSE'));
    if (!await licenseFile.exists()) {
      report.addError('LICENSE file is missing');
    }
  }
  
  static Future<void> _checkSecurity(String modulePath, QualityReport report) async {
    // Check for hardcoded secrets
    final dartFiles = await _findDartFiles(modulePath);
    final secretPatterns = [
      RegExp(r'password\s*=\s*["\'][^"\']+["\']', caseSensitive: false),
      RegExp(r'api[_-]?key\s*=\s*["\'][^"\']+["\']', caseSensitive: false),
      RegExp(r'secret\s*=\s*["\'][^"\']+["\']', caseSensitive: false),
    ];
    
    for (final file in dartFiles) {
      final content = await File(file).readAsString();
      
      for (final pattern in secretPatterns) {
        if (pattern.hasMatch(content)) {
          report.addError('Potential hardcoded secret found in $file');
        }
      }
    }
    
    // Check dependencies for known vulnerabilities
    await _checkDependencyVulnerabilities(modulePath, report);
  }
}

What's Next

You now understand the rich ecosystem of Hypermodern modules and how to create, test, and contribute your own modules. The final chapter will cover integration patterns, showing you how to integrate Hypermodern with existing systems, migrate from other frameworks, and build hybrid architectures.