-- Create events tableCREATETABLEevents(idUUIDPRIMARYKEYDEFAULTgen_random_uuid(),aggregate_idUUIDNOTNULL,aggregate_typeVARCHAR(100)NOTNULL,event_typeVARCHAR(100)NOTNULL,event_dataJSONBNOTNULL,event_metadataJSONBDEFAULT'{}',versionINTEGERNOTNULL,created_atTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP,UNIQUE(aggregate_id,version));-- Create indexes for performanceCREATEINDEXidx_events_aggregate_idONevents(aggregate_id);CREATEINDEXidx_events_aggregate_typeONevents(aggregate_type);CREATEINDEXidx_events_event_typeONevents(event_type);CREATEINDEXidx_events_created_atONevents(created_at);-- Create function to append eventCREATEORREPLACEFUNCTIONappend_event(p_aggregate_idUUID,p_aggregate_typeVARCHAR(100),p_event_typeVARCHAR(100),p_event_dataJSONB,p_event_metadataJSONBDEFAULT'{}')RETURNSUUIDAS$$DECLAREevent_idUUID;next_versionINTEGER;BEGIN-- Get next version for aggregateSELECTCOALESCE(MAX(version),0)+1INTOnext_versionFROMeventsWHEREaggregate_id=p_aggregate_id;-- Insert eventINSERTINTOevents(aggregate_id,aggregate_type,event_type,event_data,event_metadata,version)VALUES(p_aggregate_id,p_aggregate_type,p_event_type,p_event_data,p_event_metadata,next_version)RETURNINGidINTOevent_id;RETURNevent_id;END;$$LANGUAGEplpgsql;-- Create function to get events for aggregateCREATEORREPLACEFUNCTIONget_events_for_aggregate(p_aggregate_idUUID,p_from_versionINTEGERDEFAULT0)RETURNSTABLE(idUUID,aggregate_idUUID,aggregate_typeVARCHAR(100),event_typeVARCHAR(100),event_dataJSONB,event_metadataJSONB,versionINTEGER,created_atTIMESTAMPTZ)AS$$BEGINRETURNQUERYSELECTe.id,e.aggregate_id,e.aggregate_type,e.event_type,e.event_data,e.event_metadata,e.version,e.created_atFROMeventseWHEREe.aggregate_id=p_aggregate_idANDe.version>p_from_versionORDERBYe.version;END;$$LANGUAGEplpgsql;
-- Create aggregates tableCREATETABLEaggregates(idUUIDPRIMARYKEY,aggregate_typeVARCHAR(100)NOTNULL,current_versionINTEGERDEFAULT0,created_atTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP,updated_atTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP);-- Create function to create aggregateCREATEORREPLACEFUNCTIONcreate_aggregate(p_aggregate_idUUID,p_aggregate_typeVARCHAR(100))RETURNSVOIDAS$$BEGININSERTINTOaggregates(id,aggregate_type)VALUES(p_aggregate_id,p_aggregate_type)ONCONFLICT(id)DONOTHING;END;$$LANGUAGEplpgsql;-- Create function to get aggregate versionCREATEORREPLACEFUNCTIONget_aggregate_version(p_aggregate_idUUID)RETURNSINTEGERAS$$DECLAREcurrent_versionINTEGER;BEGINSELECTCOALESCE(MAX(version),0)INTOcurrent_versionFROMeventsWHEREaggregate_id=p_aggregate_id;RETURNcurrent_version;END;$$LANGUAGEplpgsql;-- Create function to update aggregate versionCREATEORREPLACEFUNCTIONupdate_aggregate_version(p_aggregate_idUUID,p_new_versionINTEGER)RETURNSVOIDAS$$BEGINUPDATEaggregatesSETcurrent_version=p_new_version,updated_at=CURRENT_TIMESTAMPWHEREid=p_aggregate_id;END;$$LANGUAGEplpgsql;
-- Create commands tableCREATETABLEcommands(idUUIDPRIMARYKEYDEFAULTgen_random_uuid(),aggregate_idUUIDNOTNULL,command_typeVARCHAR(100)NOTNULL,command_dataJSONBNOTNULL,statusVARCHAR(20)DEFAULT'pending',created_atTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP,processed_atTIMESTAMPTZ);-- Create function to create commandCREATEORREPLACEFUNCTIONcreate_command(p_aggregate_idUUID,p_command_typeVARCHAR(100),p_command_dataJSONB)RETURNSUUIDAS$$DECLAREcommand_idUUID;BEGININSERTINTOcommands(aggregate_id,command_type,command_data)VALUES(p_aggregate_id,p_command_type,p_command_data)RETURNINGidINTOcommand_id;RETURNcommand_id;END;$$LANGUAGEplpgsql;-- Create function to process commandCREATEORREPLACEFUNCTIONprocess_command(p_command_idUUID)RETURNSBOOLEANAS$$DECLAREcommand_recordRECORD;event_idUUID;BEGIN-- Get commandSELECT*INTOcommand_recordFROMcommandsWHEREid=p_command_idANDstatus='pending';IFcommand_recordISNULLTHENRETURNFALSE;ENDIF;-- Process command based on typeCASEcommand_record.command_typeWHEN'create_user'THEN-- Create user eventSELECTappend_event(command_record.aggregate_id,'user','user_created',command_record.command_data)INTOevent_id;WHEN'update_user'THEN-- Update user eventSELECTappend_event(command_record.aggregate_id,'user','user_updated',command_record.command_data)INTOevent_id;WHEN'delete_user'THEN-- Delete user eventSELECTappend_event(command_record.aggregate_id,'user','user_deleted',command_record.command_data)INTOevent_id;ELSERAISEEXCEPTION'Unknown command type: %',command_record.command_type;ENDCASE;-- Update command statusUPDATEcommandsSETstatus='processed',processed_at=CURRENT_TIMESTAMPWHEREid=p_command_id;RETURNTRUE;END;$$LANGUAGEplpgsql;
-- Create read models tableCREATETABLEread_models(idUUIDPRIMARYKEY,aggregate_idUUIDNOTNULL,aggregate_typeVARCHAR(100)NOTNULL,model_dataJSONBNOTNULL,versionINTEGERNOTNULL,created_atTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP,updated_atTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP);-- Create function to update read modelCREATEORREPLACEFUNCTIONupdate_read_model(p_aggregate_idUUID,p_aggregate_typeVARCHAR(100),p_model_dataJSONB,p_versionINTEGER)RETURNSVOIDAS$$BEGININSERTINTOread_models(id,aggregate_id,aggregate_type,model_data,version)VALUES(gen_random_uuid(),p_aggregate_id,p_aggregate_type,p_model_data,p_version)ONCONFLICT(aggregate_id)DOUPDATESETmodel_data=EXCLUDED.model_data,version=EXCLUDED.version,updated_at=CURRENT_TIMESTAMP;END;$$LANGUAGEplpgsql;-- Create function to get read modelCREATEORREPLACEFUNCTIONget_read_model(p_aggregate_idUUID)RETURNSTABLE(aggregate_idUUID,aggregate_typeVARCHAR(100),model_dataJSONB,versionINTEGER,updated_atTIMESTAMPTZ)AS$$BEGINRETURNQUERYSELECTrm.aggregate_id,rm.aggregate_type,rm.model_data,rm.version,rm.updated_atFROMread_modelsrmWHERErm.aggregate_id=p_aggregate_id;END;$$LANGUAGEplpgsql;
-- Create event streams tableCREATETABLEevent_streams(idUUIDPRIMARYKEYDEFAULTgen_random_uuid(),stream_nameVARCHAR(100)NOTNULL,event_idUUIDNOTNULL,event_typeVARCHAR(100)NOTNULL,event_dataJSONBNOTNULL,positionBIGINTNOTNULL,created_atTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP,UNIQUE(stream_name,position));-- Create function to append to streamCREATEORREPLACEFUNCTIONappend_to_stream(p_stream_nameVARCHAR(100),p_event_idUUID,p_event_typeVARCHAR(100),p_event_dataJSONB)RETURNSBIGINTAS$$DECLAREnext_positionBIGINT;BEGIN-- Get next positionSELECTCOALESCE(MAX(position),0)+1INTOnext_positionFROMevent_streamsWHEREstream_name=p_stream_name;-- Insert eventINSERTINTOevent_streams(stream_name,event_id,event_type,event_data,position)VALUES(p_stream_name,p_event_id,p_event_type,p_event_data,next_position);RETURNnext_position;END;$$LANGUAGEplpgsql;-- Create function to read from streamCREATEORREPLACEFUNCTIONread_from_stream(p_stream_nameVARCHAR(100),p_from_positionBIGINTDEFAULT0,p_limitINTEGERDEFAULT100)RETURNSTABLE(event_idUUID,event_typeVARCHAR(100),event_dataJSONB,positionBIGINT,created_atTIMESTAMPTZ)AS$$BEGINRETURNQUERYSELECTes.event_id,es.event_type,es.event_data,es.position,es.created_atFROMevent_streamsesWHEREes.stream_name=p_stream_nameANDes.position>p_from_positionORDERBYes.positionLIMITp_limit;END;$$LANGUAGEplpgsql;
-- Create event handlers tableCREATETABLEevent_handlers(idUUIDPRIMARYKEYDEFAULTgen_random_uuid(),handler_nameVARCHAR(100)UNIQUENOTNULL,event_typeVARCHAR(100)NOTNULL,handler_functionVARCHAR(100)NOTNULL,is_activeBOOLEANDEFAULTTRUE,created_atTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP);-- Create function to register event handlerCREATEORREPLACEFUNCTIONregister_event_handler(p_handler_nameVARCHAR(100),p_event_typeVARCHAR(100),p_handler_functionVARCHAR(100))RETURNSVOIDAS$$BEGININSERTINTOevent_handlers(handler_name,event_type,handler_function)VALUES(p_handler_name,p_event_type,p_handler_function)ONCONFLICT(handler_name)DOUPDATESETevent_type=EXCLUDED.event_type,handler_function=EXCLUDED.handler_function;END;$$LANGUAGEplpgsql;-- Create function to process eventCREATEORREPLACEFUNCTIONprocess_event(p_event_idUUID)RETURNSBOOLEANAS$$DECLAREevent_recordRECORD;handler_recordRECORD;BEGIN-- Get eventSELECT*INTOevent_recordFROMeventsWHEREid=p_event_id;IFevent_recordISNULLTHENRETURNFALSE;ENDIF;-- Get handlers for event typeFORhandler_recordINSELECT*FROMevent_handlersWHEREevent_type=event_record.event_typeANDis_active=TRUELOOP-- Execute handler functionEXECUTEformat('SELECT %s(%s, %s, %s)',handler_record.handler_function,event_record.id,event_record.aggregate_id,event_record.event_data);ENDLOOP;RETURNTRUE;END;$$LANGUAGEplpgsql;
-- Create event metrics tableCREATETABLEevent_metrics(idSERIALPRIMARYKEY,event_typeVARCHAR(100)NOTNULL,aggregate_typeVARCHAR(100)NOTNULL,event_countBIGINTDEFAULT1,processing_time_msINTEGER,created_atTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP);-- Create function to record event metricsCREATEORREPLACEFUNCTIONrecord_event_metrics(p_event_typeVARCHAR(100),p_aggregate_typeVARCHAR(100),p_processing_time_msINTEGERDEFAULTNULL)RETURNSVOIDAS$$BEGININSERTINTOevent_metrics(event_type,aggregate_type,processing_time_ms)VALUES(p_event_type,p_aggregate_type,p_processing_time_ms);END;$$LANGUAGEplpgsql;-- Create function to get event metricsCREATEORREPLACEFUNCTIONget_event_metrics(p_start_dateTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP-INTERVAL'1 hour',p_end_dateTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP)RETURNSTABLE(event_typeVARCHAR(100),aggregate_typeVARCHAR(100),event_countBIGINT,avg_processing_timeNUMERIC,max_processing_timeINTEGER,min_processing_timeINTEGER)AS$$BEGINRETURNQUERYSELECTem.event_type,em.aggregate_type,COUNT(*)asevent_count,AVG(em.processing_time_ms)asavg_processing_time,MAX(em.processing_time_ms)asmax_processing_time,MIN(em.processing_time_ms)asmin_processing_timeFROMevent_metricsemWHEREem.created_atBETWEENp_start_dateANDp_end_dateGROUPBYem.event_type,em.aggregate_typeORDERBYevent_countDESC;END;$$LANGUAGEplpgsql;