Skip to main content

allframe_forge/
config.rs

1//! Project configuration for code generation
2//!
3//! This module defines the configuration structures used by AllFrame Ignite
4//! to generate different types of projects and archetypes.
5
6use std::time::Duration;
7
8use serde::{Deserialize, Serialize};
9
10/// Project archetype - defines the type of service to generate
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
12#[serde(rename_all = "snake_case")]
13pub enum Archetype {
14    /// Basic Clean Architecture project (default)
15    #[default]
16    Basic,
17    /// API Gateway service with gRPC, resilience, and caching
18    Gateway,
19    /// Event-sourced CQRS service
20    EventSourced,
21    /// Consumer service (event handler)
22    Consumer,
23    /// Producer service (event publisher)
24    Producer,
25    /// Backend for Frontend - API aggregation layer
26    Bff,
27    /// Scheduled job service (cron jobs, periodic tasks)
28    Scheduled,
29    /// WebSocket gateway for real-time streaming
30    WebSocketGateway,
31    /// Saga orchestrator for distributed transactions
32    SagaOrchestrator,
33    /// Anti-corruption layer for legacy system integration
34    AntiCorruptionLayer,
35}
36
37impl std::fmt::Display for Archetype {
38    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39        match self {
40            Self::Basic => write!(f, "basic"),
41            Self::Gateway => write!(f, "gateway"),
42            Self::EventSourced => write!(f, "event-sourced"),
43            Self::Consumer => write!(f, "consumer"),
44            Self::Producer => write!(f, "producer"),
45            Self::Bff => write!(f, "bff"),
46            Self::Scheduled => write!(f, "scheduled"),
47            Self::WebSocketGateway => write!(f, "websocket-gateway"),
48            Self::SagaOrchestrator => write!(f, "saga-orchestrator"),
49            Self::AntiCorruptionLayer => write!(f, "anti-corruption-layer"),
50        }
51    }
52}
53
54impl std::str::FromStr for Archetype {
55    type Err = String;
56
57    fn from_str(s: &str) -> Result<Self, Self::Err> {
58        match s.to_lowercase().as_str() {
59            "basic" => Ok(Self::Basic),
60            "gateway" => Ok(Self::Gateway),
61            "event-sourced" | "eventsourced" | "cqrs" => Ok(Self::EventSourced),
62            "consumer" => Ok(Self::Consumer),
63            "producer" => Ok(Self::Producer),
64            "bff" | "backend-for-frontend" => Ok(Self::Bff),
65            "scheduled" | "cron" | "jobs" => Ok(Self::Scheduled),
66            "websocket-gateway" | "websocket" | "ws" => Ok(Self::WebSocketGateway),
67            "saga-orchestrator" | "saga" => Ok(Self::SagaOrchestrator),
68            "anti-corruption-layer" | "acl" | "legacy-adapter" => Ok(Self::AntiCorruptionLayer),
69            _ => Err(format!("Unknown archetype: {}", s)),
70        }
71    }
72}
73
74/// Protocol support for the service
75#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
76#[serde(rename_all = "lowercase")]
77pub enum Protocol {
78    /// REST API
79    Rest,
80    /// GraphQL API
81    Graphql,
82    /// gRPC API
83    Grpc,
84}
85
86/// Authentication method for external APIs
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
88#[serde(rename_all = "snake_case")]
89pub enum AuthMethod {
90    /// No authentication
91    #[default]
92    None,
93    /// API Key in header
94    ApiKey,
95    /// HMAC-SHA256 signature
96    HmacSha256,
97    /// HMAC-SHA512 with Base64 encoding
98    HmacSha512Base64,
99    /// OAuth2
100    OAuth2,
101    /// JWT Bearer token
102    Jwt,
103}
104
105/// Cache backend selection
106#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
107#[serde(rename_all = "lowercase")]
108pub enum CacheBackend {
109    /// In-memory cache (moka)
110    #[default]
111    Memory,
112    /// Redis cache
113    Redis,
114    /// No caching
115    None,
116}
117
118/// Message broker type for event-driven services
119#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
120#[serde(rename_all = "lowercase")]
121pub enum MessageBroker {
122    /// Apache Kafka
123    #[default]
124    Kafka,
125    /// RabbitMQ
126    RabbitMq,
127    /// Redis Streams
128    Redis,
129    /// AWS SQS
130    Sqs,
131    /// Google Cloud Pub/Sub
132    PubSub,
133}
134
135impl std::fmt::Display for MessageBroker {
136    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137        match self {
138            Self::Kafka => write!(f, "kafka"),
139            Self::RabbitMq => write!(f, "rabbitmq"),
140            Self::Redis => write!(f, "redis"),
141            Self::Sqs => write!(f, "sqs"),
142            Self::PubSub => write!(f, "pubsub"),
143        }
144    }
145}
146
147/// Main project configuration
148#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct ProjectConfig {
150    /// Project name (used for crate name, directories)
151    pub name: String,
152    /// Project archetype
153    #[serde(default)]
154    pub archetype: Archetype,
155    /// Protocols to expose
156    #[serde(default)]
157    pub protocols: Vec<Protocol>,
158    /// Enable OpenTelemetry tracing
159    #[serde(default = "default_true")]
160    pub tracing: bool,
161    /// Enable Prometheus metrics
162    #[serde(default = "default_true")]
163    pub metrics: bool,
164    /// Gateway-specific configuration
165    #[serde(default)]
166    pub gateway: Option<GatewayConfig>,
167    /// Consumer-specific configuration
168    #[serde(default)]
169    pub consumer: Option<ConsumerConfig>,
170    /// Producer-specific configuration
171    #[serde(default)]
172    pub producer: Option<ProducerConfig>,
173    /// BFF-specific configuration
174    #[serde(default)]
175    pub bff: Option<BffConfig>,
176    /// Scheduled jobs configuration
177    #[serde(default)]
178    pub scheduled: Option<ScheduledConfig>,
179    /// WebSocket gateway configuration
180    #[serde(default)]
181    pub websocket_gateway: Option<WebSocketGatewayConfig>,
182    /// Saga orchestrator configuration
183    #[serde(default)]
184    pub saga_orchestrator: Option<SagaOrchestratorConfig>,
185    /// Anti-corruption layer configuration
186    #[serde(default)]
187    pub acl: Option<AntiCorruptionLayerConfig>,
188}
189
190fn default_true() -> bool {
191    true
192}
193
194impl Default for ProjectConfig {
195    fn default() -> Self {
196        Self {
197            name: String::new(),
198            archetype: Archetype::default(),
199            protocols: vec![Protocol::Grpc],
200            tracing: true,
201            metrics: true,
202            gateway: None,
203            consumer: None,
204            producer: None,
205            bff: None,
206            scheduled: None,
207            websocket_gateway: None,
208            saga_orchestrator: None,
209            acl: None,
210        }
211    }
212}
213
214impl ProjectConfig {
215    /// Create a new project configuration
216    pub fn new(name: impl Into<String>) -> Self {
217        Self {
218            name: name.into(),
219            ..Default::default()
220        }
221    }
222
223    /// Set the archetype
224    pub fn with_archetype(mut self, archetype: Archetype) -> Self {
225        self.archetype = archetype;
226        match archetype {
227            Archetype::Gateway if self.gateway.is_none() => {
228                self.gateway = Some(GatewayConfig::default());
229            }
230            Archetype::Consumer if self.consumer.is_none() => {
231                self.consumer = Some(ConsumerConfig::default());
232            }
233            Archetype::Producer if self.producer.is_none() => {
234                self.producer = Some(ProducerConfig::default());
235            }
236            Archetype::Bff if self.bff.is_none() => {
237                self.bff = Some(BffConfig::default());
238            }
239            Archetype::Scheduled if self.scheduled.is_none() => {
240                self.scheduled = Some(ScheduledConfig::default());
241            }
242            Archetype::WebSocketGateway if self.websocket_gateway.is_none() => {
243                self.websocket_gateway = Some(WebSocketGatewayConfig::default());
244            }
245            Archetype::SagaOrchestrator if self.saga_orchestrator.is_none() => {
246                self.saga_orchestrator = Some(SagaOrchestratorConfig::default());
247            }
248            Archetype::AntiCorruptionLayer if self.acl.is_none() => {
249                self.acl = Some(AntiCorruptionLayerConfig::default());
250            }
251            _ => {}
252        }
253        self
254    }
255
256    /// Set the protocols
257    pub fn with_protocols(mut self, protocols: Vec<Protocol>) -> Self {
258        self.protocols = protocols;
259        self
260    }
261
262    /// Set gateway configuration
263    pub fn with_gateway(mut self, gateway: GatewayConfig) -> Self {
264        self.gateway = Some(gateway);
265        self
266    }
267
268    /// Set consumer configuration
269    pub fn with_consumer(mut self, consumer: ConsumerConfig) -> Self {
270        self.consumer = Some(consumer);
271        self
272    }
273
274    /// Set producer configuration
275    pub fn with_producer(mut self, producer: ProducerConfig) -> Self {
276        self.producer = Some(producer);
277        self
278    }
279
280    /// Set BFF configuration
281    pub fn with_bff(mut self, bff: BffConfig) -> Self {
282        self.bff = Some(bff);
283        self
284    }
285
286    /// Set scheduled jobs configuration
287    pub fn with_scheduled(mut self, scheduled: ScheduledConfig) -> Self {
288        self.scheduled = Some(scheduled);
289        self
290    }
291
292    /// Set WebSocket gateway configuration
293    pub fn with_websocket_gateway(mut self, ws: WebSocketGatewayConfig) -> Self {
294        self.websocket_gateway = Some(ws);
295        self
296    }
297
298    /// Set saga orchestrator configuration
299    pub fn with_saga_orchestrator(mut self, saga: SagaOrchestratorConfig) -> Self {
300        self.saga_orchestrator = Some(saga);
301        self
302    }
303
304    /// Set anti-corruption layer configuration
305    pub fn with_acl(mut self, acl: AntiCorruptionLayerConfig) -> Self {
306        self.acl = Some(acl);
307        self
308    }
309}
310
311/// Gateway-specific configuration
312#[derive(Debug, Clone, Serialize, Deserialize)]
313pub struct GatewayConfig {
314    /// Service name (e.g., "kraken", "binance")
315    pub service_name: String,
316    /// Human-readable display name
317    pub display_name: String,
318    /// Base URL for the external API
319    pub api_base_url: String,
320    /// Authentication method
321    #[serde(default)]
322    pub auth_method: AuthMethod,
323    /// Rate limiting configuration
324    #[serde(default)]
325    pub rate_limit: RateLimitConfig,
326    /// Cache configuration
327    #[serde(default)]
328    pub cache: CacheConfig,
329    /// Server configuration
330    #[serde(default)]
331    pub server: ServerConfig,
332    /// Entity definitions for the domain
333    #[serde(default)]
334    pub entities: Vec<EntityConfig>,
335    /// API endpoints to wrap
336    #[serde(default)]
337    pub endpoints: Vec<EndpointConfig>,
338}
339
340impl Default for GatewayConfig {
341    fn default() -> Self {
342        Self {
343            service_name: "exchange".to_string(),
344            display_name: "Exchange Gateway".to_string(),
345            api_base_url: "https://api.example.com".to_string(),
346            auth_method: AuthMethod::default(),
347            rate_limit: RateLimitConfig::default(),
348            cache: CacheConfig::default(),
349            server: ServerConfig::default(),
350            entities: vec![],
351            endpoints: vec![],
352        }
353    }
354}
355
356/// Rate limiting configuration
357#[derive(Debug, Clone, Serialize, Deserialize)]
358pub struct RateLimitConfig {
359    /// Requests per second for public endpoints
360    pub public_rps: u32,
361    /// Requests per second for private endpoints
362    pub private_rps: u32,
363    /// Burst size
364    pub burst: u32,
365}
366
367impl Default for RateLimitConfig {
368    fn default() -> Self {
369        Self {
370            public_rps: 10,
371            private_rps: 2,
372            burst: 5,
373        }
374    }
375}
376
377/// Cache configuration
378#[derive(Debug, Clone, Serialize, Deserialize)]
379pub struct CacheConfig {
380    /// Enable caching
381    #[serde(default = "default_true")]
382    pub enabled: bool,
383    /// Cache backend
384    #[serde(default)]
385    pub backend: CacheBackend,
386    /// TTL for public data in seconds
387    #[serde(default = "default_public_ttl")]
388    pub public_ttl_secs: u64,
389    /// TTL for private data in seconds
390    #[serde(default = "default_private_ttl")]
391    pub private_ttl_secs: u64,
392}
393
394fn default_public_ttl() -> u64 {
395    300
396}
397
398fn default_private_ttl() -> u64 {
399    60
400}
401
402impl Default for CacheConfig {
403    fn default() -> Self {
404        Self {
405            enabled: true,
406            backend: CacheBackend::Memory,
407            public_ttl_secs: 300,
408            private_ttl_secs: 60,
409        }
410    }
411}
412
413impl CacheConfig {
414    /// Get public TTL as Duration
415    pub fn public_ttl(&self) -> Duration {
416        Duration::from_secs(self.public_ttl_secs)
417    }
418
419    /// Get private TTL as Duration
420    pub fn private_ttl(&self) -> Duration {
421        Duration::from_secs(self.private_ttl_secs)
422    }
423}
424
425/// Server configuration
426#[derive(Debug, Clone, Serialize, Deserialize)]
427pub struct ServerConfig {
428    /// HTTP server port (for REST APIs)
429    #[serde(default = "default_http_port")]
430    pub http_port: u16,
431    /// gRPC server port
432    #[serde(default = "default_grpc_port")]
433    pub grpc_port: u16,
434    /// Health check port
435    #[serde(default = "default_health_port")]
436    pub health_port: u16,
437    /// Metrics port
438    #[serde(default = "default_metrics_port")]
439    pub metrics_port: u16,
440}
441
442fn default_http_port() -> u16 {
443    8080
444}
445
446fn default_grpc_port() -> u16 {
447    50051
448}
449
450fn default_health_port() -> u16 {
451    8081
452}
453
454fn default_metrics_port() -> u16 {
455    9090
456}
457
458impl Default for ServerConfig {
459    fn default() -> Self {
460        Self {
461            http_port: 8080,
462            grpc_port: 50051,
463            health_port: 8081,
464            metrics_port: 9090,
465        }
466    }
467}
468
469/// Entity definition for domain model generation
470#[derive(Debug, Clone, Serialize, Deserialize)]
471pub struct EntityConfig {
472    /// Entity name (e.g., "Asset", "Balance", "Trade")
473    pub name: String,
474    /// Fields for the entity
475    pub fields: Vec<FieldConfig>,
476}
477
478/// Field definition for entities
479#[derive(Debug, Clone, Serialize, Deserialize)]
480pub struct FieldConfig {
481    /// Field name
482    pub name: String,
483    /// Field type (Rust type)
484    pub field_type: String,
485    /// Is this field optional?
486    #[serde(default)]
487    pub optional: bool,
488}
489
490/// API endpoint configuration
491#[derive(Debug, Clone, Serialize, Deserialize)]
492pub struct EndpointConfig {
493    /// Endpoint name (used for method names)
494    pub name: String,
495    /// HTTP method (GET, POST, etc.)
496    pub method: String,
497    /// Endpoint path
498    pub path: String,
499    /// Is this a private endpoint requiring auth?
500    #[serde(default)]
501    pub private: bool,
502    /// Request parameters
503    #[serde(default)]
504    pub params: Vec<ParamConfig>,
505    /// Response type
506    pub response_type: String,
507    /// Should responses be cached?
508    #[serde(default)]
509    pub cacheable: bool,
510}
511
512/// Parameter configuration for endpoints
513#[derive(Debug, Clone, Serialize, Deserialize)]
514pub struct ParamConfig {
515    /// Parameter name
516    pub name: String,
517    /// Parameter type
518    pub param_type: String,
519    /// Is this parameter required?
520    #[serde(default = "default_true")]
521    pub required: bool,
522}
523
524/// Consumer-specific configuration for event handler services
525#[derive(Debug, Clone, Serialize, Deserialize)]
526pub struct ConsumerConfig {
527    /// Service name (e.g., "order-processor", "notification-handler")
528    pub service_name: String,
529    /// Human-readable display name
530    pub display_name: String,
531    /// Message broker type
532    #[serde(default)]
533    pub broker: MessageBroker,
534    /// Topics/queues to consume from
535    pub topics: Vec<TopicConfig>,
536    /// Consumer group ID
537    pub group_id: String,
538    /// Dead Letter Queue configuration
539    #[serde(default)]
540    pub dlq: DlqConfig,
541    /// Retry configuration
542    #[serde(default)]
543    pub retry: RetryConfig,
544    /// Idempotency configuration
545    #[serde(default)]
546    pub idempotency: IdempotencyConfig,
547    /// Server configuration (health, metrics)
548    #[serde(default)]
549    pub server: ServerConfig,
550}
551
552impl Default for ConsumerConfig {
553    fn default() -> Self {
554        Self {
555            service_name: "consumer".to_string(),
556            display_name: "Event Consumer".to_string(),
557            broker: MessageBroker::default(),
558            topics: vec![TopicConfig::default()],
559            group_id: "consumer-group".to_string(),
560            dlq: DlqConfig::default(),
561            retry: RetryConfig::default(),
562            idempotency: IdempotencyConfig::default(),
563            server: ServerConfig::default(),
564        }
565    }
566}
567
568/// Topic/queue configuration for consumers
569#[derive(Debug, Clone, Serialize, Deserialize)]
570pub struct TopicConfig {
571    /// Topic/queue name
572    pub name: String,
573    /// Event type expected on this topic
574    pub event_type: String,
575    /// Number of partitions (for Kafka)
576    #[serde(default = "default_partitions")]
577    pub partitions: u32,
578    /// Whether to auto-commit offsets
579    #[serde(default)]
580    pub auto_commit: bool,
581}
582
583fn default_partitions() -> u32 {
584    1
585}
586
587impl Default for TopicConfig {
588    fn default() -> Self {
589        Self {
590            name: "events".to_string(),
591            event_type: "Event".to_string(),
592            partitions: 1,
593            auto_commit: false,
594        }
595    }
596}
597
598/// Dead Letter Queue configuration
599#[derive(Debug, Clone, Serialize, Deserialize)]
600pub struct DlqConfig {
601    /// Enable DLQ
602    #[serde(default = "default_true")]
603    pub enabled: bool,
604    /// DLQ topic/queue name suffix
605    #[serde(default = "default_dlq_suffix")]
606    pub suffix: String,
607    /// Max retries before sending to DLQ
608    #[serde(default = "default_max_retries")]
609    pub max_retries: u32,
610}
611
612fn default_dlq_suffix() -> String {
613    ".dlq".to_string()
614}
615
616fn default_max_retries() -> u32 {
617    3
618}
619
620impl Default for DlqConfig {
621    fn default() -> Self {
622        Self {
623            enabled: true,
624            suffix: ".dlq".to_string(),
625            max_retries: 3,
626        }
627    }
628}
629
630/// Retry configuration for message processing
631#[derive(Debug, Clone, Serialize, Deserialize)]
632pub struct RetryConfig {
633    /// Maximum number of retry attempts
634    #[serde(default = "default_max_retries")]
635    pub max_attempts: u32,
636    /// Initial backoff delay in milliseconds
637    #[serde(default = "default_initial_backoff")]
638    pub initial_backoff_ms: u64,
639    /// Maximum backoff delay in milliseconds
640    #[serde(default = "default_max_backoff")]
641    pub max_backoff_ms: u64,
642    /// Backoff multiplier
643    #[serde(default = "default_backoff_multiplier")]
644    pub multiplier: f64,
645}
646
647fn default_initial_backoff() -> u64 {
648    100
649}
650
651fn default_max_backoff() -> u64 {
652    10000
653}
654
655fn default_backoff_multiplier() -> f64 {
656    2.0
657}
658
659impl Default for RetryConfig {
660    fn default() -> Self {
661        Self {
662            max_attempts: 3,
663            initial_backoff_ms: 100,
664            max_backoff_ms: 10000,
665            multiplier: 2.0,
666        }
667    }
668}
669
670/// Idempotency configuration
671#[derive(Debug, Clone, Serialize, Deserialize)]
672pub struct IdempotencyConfig {
673    /// Enable idempotency checking
674    #[serde(default = "default_true")]
675    pub enabled: bool,
676    /// Storage backend for idempotency keys
677    #[serde(default)]
678    pub storage: IdempotencyStorage,
679    /// TTL for idempotency keys in seconds
680    #[serde(default = "default_idempotency_ttl")]
681    pub ttl_secs: u64,
682}
683
684fn default_idempotency_ttl() -> u64 {
685    86400 // 24 hours
686}
687
688impl Default for IdempotencyConfig {
689    fn default() -> Self {
690        Self {
691            enabled: true,
692            storage: IdempotencyStorage::default(),
693            ttl_secs: 86400,
694        }
695    }
696}
697
698/// Storage backend for idempotency keys
699#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
700#[serde(rename_all = "lowercase")]
701pub enum IdempotencyStorage {
702    /// In-memory storage (for development)
703    #[default]
704    Memory,
705    /// Redis
706    Redis,
707    /// PostgreSQL
708    Postgres,
709}
710
711/// Producer-specific configuration for event publishing services
712#[derive(Debug, Clone, Serialize, Deserialize)]
713pub struct ProducerConfig {
714    /// Service name
715    pub service_name: String,
716    /// Human-readable display name
717    pub display_name: String,
718    /// Message broker type
719    #[serde(default)]
720    pub broker: MessageBroker,
721    /// Topics to publish to
722    pub topics: Vec<TopicConfig>,
723    /// Outbox pattern configuration
724    #[serde(default)]
725    pub outbox: OutboxConfig,
726    /// Server configuration
727    #[serde(default)]
728    pub server: ServerConfig,
729}
730
731impl Default for ProducerConfig {
732    fn default() -> Self {
733        Self {
734            service_name: "producer".to_string(),
735            display_name: "Event Producer".to_string(),
736            broker: MessageBroker::default(),
737            topics: vec![TopicConfig::default()],
738            outbox: OutboxConfig::default(),
739            server: ServerConfig::default(),
740        }
741    }
742}
743
744/// Outbox pattern configuration for reliable event publishing
745#[derive(Debug, Clone, Serialize, Deserialize)]
746pub struct OutboxConfig {
747    /// Enable outbox pattern
748    #[serde(default = "default_true")]
749    pub enabled: bool,
750    /// Outbox table name
751    #[serde(default = "default_outbox_table")]
752    pub table_name: String,
753    /// Polling interval in milliseconds
754    #[serde(default = "default_polling_interval")]
755    pub polling_interval_ms: u64,
756    /// Batch size for outbox processing
757    #[serde(default = "default_batch_size")]
758    pub batch_size: u32,
759    /// Maximum retries before giving up on an outbox entry
760    #[serde(default = "default_max_retries")]
761    pub max_retries: u32,
762}
763
764fn default_outbox_table() -> String {
765    "outbox".to_string()
766}
767
768fn default_polling_interval() -> u64 {
769    1000
770}
771
772fn default_batch_size() -> u32 {
773    100
774}
775
776impl Default for OutboxConfig {
777    fn default() -> Self {
778        Self {
779            enabled: true,
780            table_name: "outbox".to_string(),
781            polling_interval_ms: 1000,
782            batch_size: 100,
783            max_retries: 3,
784        }
785    }
786}
787
788/// BFF (Backend for Frontend) configuration
789#[derive(Debug, Clone, Serialize, Deserialize)]
790pub struct BffConfig {
791    /// Service name (e.g., "web-bff", "mobile-bff")
792    pub service_name: String,
793    /// Display name for documentation
794    pub display_name: String,
795    /// Target frontend type
796    pub frontend_type: FrontendType,
797    /// Backend services to aggregate
798    pub backends: Vec<BackendServiceConfig>,
799    /// Enable GraphQL support
800    pub graphql_enabled: bool,
801    /// Enable REST API
802    pub rest_enabled: bool,
803    /// Cache configuration
804    pub cache: CacheConfig,
805    /// Server configuration
806    pub server: ServerConfig,
807}
808
809/// Target frontend type for BFF
810#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
811pub enum FrontendType {
812    /// Web browser frontend
813    #[default]
814    Web,
815    /// Mobile app frontend (iOS/Android)
816    Mobile,
817    /// Desktop application frontend
818    Desktop,
819    /// Command-line interface
820    Cli,
821}
822
823/// Backend service configuration for BFF
824#[derive(Debug, Clone, Serialize, Deserialize)]
825pub struct BackendServiceConfig {
826    /// Service name
827    pub name: String,
828    /// Base URL of the backend service
829    pub base_url: String,
830    /// Timeout in milliseconds
831    pub timeout_ms: u64,
832    /// Enable circuit breaker
833    pub circuit_breaker: bool,
834}
835
836impl Default for BffConfig {
837    fn default() -> Self {
838        Self {
839            service_name: "bff".to_string(),
840            display_name: "Backend for Frontend".to_string(),
841            frontend_type: FrontendType::Web,
842            backends: vec![BackendServiceConfig {
843                name: "api".to_string(),
844                base_url: "http://localhost:8080".to_string(),
845                timeout_ms: 5000,
846                circuit_breaker: true,
847            }],
848            graphql_enabled: true,
849            rest_enabled: true,
850            cache: CacheConfig::default(),
851            server: ServerConfig::default(),
852        }
853    }
854}
855
856/// Scheduled job service configuration
857#[derive(Debug, Clone, Serialize, Deserialize)]
858pub struct ScheduledConfig {
859    /// Service name
860    pub service_name: String,
861    /// Display name for documentation
862    pub display_name: String,
863    /// Jobs to schedule
864    pub jobs: Vec<JobConfig>,
865    /// Server configuration (for health checks)
866    pub server: ServerConfig,
867}
868
869/// Individual job configuration
870#[derive(Debug, Clone, Serialize, Deserialize)]
871pub struct JobConfig {
872    /// Job name
873    pub name: String,
874    /// Cron expression (e.g., "0 0 * * *" for daily at midnight)
875    pub cron: String,
876    /// Job description
877    pub description: String,
878    /// Enable job by default
879    pub enabled: bool,
880    /// Timeout in seconds
881    pub timeout_secs: u64,
882}
883
884impl Default for ScheduledConfig {
885    fn default() -> Self {
886        Self {
887            service_name: "scheduler".to_string(),
888            display_name: "Scheduled Jobs".to_string(),
889            jobs: vec![JobConfig {
890                name: "cleanup".to_string(),
891                cron: "0 0 * * *".to_string(),
892                description: "Daily cleanup job".to_string(),
893                enabled: true,
894                timeout_secs: 300,
895            }],
896            server: ServerConfig::default(),
897        }
898    }
899}
900
901/// WebSocket gateway configuration
902#[derive(Debug, Clone, Serialize, Deserialize)]
903pub struct WebSocketGatewayConfig {
904    /// Service name
905    pub service_name: String,
906    /// Display name for documentation
907    pub display_name: String,
908    /// Channels/rooms configuration
909    pub channels: Vec<ChannelConfig>,
910    /// Maximum connections per client
911    pub max_connections_per_client: u32,
912    /// Heartbeat interval in seconds
913    pub heartbeat_interval_secs: u64,
914    /// Connection timeout in seconds
915    pub connection_timeout_secs: u64,
916    /// Server configuration
917    pub server: ServerConfig,
918}
919
920/// Channel configuration for WebSocket
921#[derive(Debug, Clone, Serialize, Deserialize)]
922pub struct ChannelConfig {
923    /// Channel name
924    pub name: String,
925    /// Channel description
926    pub description: String,
927    /// Require authentication
928    pub authenticated: bool,
929}
930
931impl Default for WebSocketGatewayConfig {
932    fn default() -> Self {
933        Self {
934            service_name: "ws_gateway".to_string(),
935            display_name: "WebSocket Gateway".to_string(),
936            channels: vec![ChannelConfig {
937                name: "events".to_string(),
938                description: "Real-time event stream".to_string(),
939                authenticated: true,
940            }],
941            max_connections_per_client: 5,
942            heartbeat_interval_secs: 30,
943            connection_timeout_secs: 60,
944            server: ServerConfig::default(),
945        }
946    }
947}
948
949/// Saga orchestrator configuration
950#[derive(Debug, Clone, Serialize, Deserialize)]
951pub struct SagaOrchestratorConfig {
952    /// Service name
953    pub service_name: String,
954    /// Display name for documentation
955    pub display_name: String,
956    /// Saga definitions
957    pub sagas: Vec<SagaDefinitionConfig>,
958    /// Retry configuration
959    pub retry: RetryConfig,
960    /// Server configuration
961    pub server: ServerConfig,
962}
963
964/// Saga definition configuration
965#[derive(Debug, Clone, Serialize, Deserialize)]
966pub struct SagaDefinitionConfig {
967    /// Saga name
968    pub name: String,
969    /// Saga description
970    pub description: String,
971    /// Steps in the saga
972    pub steps: Vec<String>,
973}
974
975impl Default for SagaOrchestratorConfig {
976    fn default() -> Self {
977        Self {
978            service_name: "saga_orchestrator".to_string(),
979            display_name: "Saga Orchestrator".to_string(),
980            sagas: vec![SagaDefinitionConfig {
981                name: "order_saga".to_string(),
982                description: "Order processing saga".to_string(),
983                steps: vec![
984                    "validate_order".to_string(),
985                    "reserve_inventory".to_string(),
986                    "process_payment".to_string(),
987                    "send_notification".to_string(),
988                ],
989            }],
990            retry: RetryConfig::default(),
991            server: ServerConfig::default(),
992        }
993    }
994}
995
996/// Anti-corruption layer configuration
997#[derive(Debug, Clone, Serialize, Deserialize)]
998pub struct AntiCorruptionLayerConfig {
999    /// Service name
1000    pub service_name: String,
1001    /// Display name for documentation
1002    pub display_name: String,
1003    /// Legacy system configuration
1004    pub legacy_system: LegacySystemConfig,
1005    /// Transformation rules
1006    pub transformations: Vec<TransformationConfig>,
1007    /// Server configuration
1008    pub server: ServerConfig,
1009}
1010
1011/// Legacy system connection configuration
1012#[derive(Debug, Clone, Serialize, Deserialize)]
1013pub struct LegacySystemConfig {
1014    /// System name
1015    pub name: String,
1016    /// Connection type (REST, SOAP, Database, etc.)
1017    pub connection_type: LegacyConnectionType,
1018    /// Base URL or connection string
1019    pub connection_string: String,
1020    /// Timeout in milliseconds
1021    pub timeout_ms: u64,
1022}
1023
1024/// Legacy system connection type
1025#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1026pub enum LegacyConnectionType {
1027    /// REST API connection
1028    #[default]
1029    Rest,
1030    /// SOAP/XML web service
1031    Soap,
1032    /// Direct database connection
1033    Database,
1034    /// File-based integration
1035    File,
1036    /// Message queue integration
1037    Mq,
1038}
1039
1040/// Transformation rule configuration
1041#[derive(Debug, Clone, Serialize, Deserialize)]
1042pub struct TransformationConfig {
1043    /// Source entity name (legacy)
1044    pub source: String,
1045    /// Target entity name (modern)
1046    pub target: String,
1047    /// Description of the transformation
1048    pub description: String,
1049}
1050
1051impl Default for AntiCorruptionLayerConfig {
1052    fn default() -> Self {
1053        Self {
1054            service_name: "acl".to_string(),
1055            display_name: "Anti-Corruption Layer".to_string(),
1056            legacy_system: LegacySystemConfig {
1057                name: "legacy_api".to_string(),
1058                connection_type: LegacyConnectionType::Rest,
1059                connection_string: "http://legacy-system:8080".to_string(),
1060                timeout_ms: 10000,
1061            },
1062            transformations: vec![TransformationConfig {
1063                source: "LegacyEntity".to_string(),
1064                target: "ModernEntity".to_string(),
1065                description: "Transform legacy entity to modern format".to_string(),
1066            }],
1067            server: ServerConfig::default(),
1068        }
1069    }
1070}
1071
1072#[cfg(test)]
1073mod tests {
1074    use super::*;
1075
1076    #[test]
1077    fn test_archetype_from_str() {
1078        assert_eq!("basic".parse::<Archetype>().unwrap(), Archetype::Basic);
1079        assert_eq!("gateway".parse::<Archetype>().unwrap(), Archetype::Gateway);
1080        assert_eq!(
1081            "event-sourced".parse::<Archetype>().unwrap(),
1082            Archetype::EventSourced
1083        );
1084        assert_eq!(
1085            "cqrs".parse::<Archetype>().unwrap(),
1086            Archetype::EventSourced
1087        );
1088        assert_eq!(
1089            "consumer".parse::<Archetype>().unwrap(),
1090            Archetype::Consumer
1091        );
1092        assert_eq!(
1093            "producer".parse::<Archetype>().unwrap(),
1094            Archetype::Producer
1095        );
1096    }
1097
1098    #[test]
1099    fn test_project_config_default() {
1100        let config = ProjectConfig::new("test-project");
1101        assert_eq!(config.name, "test-project");
1102        assert_eq!(config.archetype, Archetype::Basic);
1103        assert!(config.tracing);
1104        assert!(config.metrics);
1105    }
1106
1107    #[test]
1108    fn test_gateway_config_default() {
1109        let config = GatewayConfig::default();
1110        assert_eq!(config.rate_limit.public_rps, 10);
1111        assert_eq!(config.cache.public_ttl_secs, 300);
1112    }
1113
1114    #[test]
1115    fn test_consumer_config_default() {
1116        let config = ConsumerConfig::default();
1117        assert_eq!(config.broker, MessageBroker::Kafka);
1118        assert!(config.dlq.enabled);
1119        assert!(config.idempotency.enabled);
1120        assert_eq!(config.retry.max_attempts, 3);
1121    }
1122
1123    #[test]
1124    fn test_producer_config_default() {
1125        let config = ProducerConfig::default();
1126        assert_eq!(config.broker, MessageBroker::Kafka);
1127        assert!(config.outbox.enabled);
1128        assert_eq!(config.outbox.batch_size, 100);
1129    }
1130
1131    #[test]
1132    fn test_with_archetype_consumer() {
1133        let config = ProjectConfig::new("test").with_archetype(Archetype::Consumer);
1134        assert!(config.consumer.is_some());
1135    }
1136
1137    #[test]
1138    fn test_with_archetype_producer() {
1139        let config = ProjectConfig::new("test").with_archetype(Archetype::Producer);
1140        assert!(config.producer.is_some());
1141    }
1142}