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