k, let's imagine we want to do 3 (auto-register domains) and 4 (extract generated files from repo). Give some really concrete examples of how we would do that, and why it's better.
The user wants concrete examples of how to implement: 1. Auto-register domains (instead of editing server_init.go) 2. Extract generated files from repo (with gitattributes merge strategy)
Let me look at the current patterns in more detail to give really concrete examples.
Let me look at the current patterns more closely to give you concrete before/after examples.
{
"file_path": "/Users/willlarson/go-backend/ledger/internal/server/server_init.go",
"offset": 400,
"limit": 200
}{
"file_path": "/Users/willlarson/go-backend/ledger/internal/server/server.go",
"limit": 150
} 400→ return err
401→ }
402→ cfg.PrivateSSHKey = resolvedPrivateSSHKey
403→ febSftpClient, err := sftp_impl.NewSFTPHandler(cfg, sftp_impl.WithLogger(s.logger), sftp_impl.WithStats(s.stats))
404→ if err != nil {
405→ return err
406→ }
407→
408→ s.febSftpClient = febSftpClient
409→ return nil
410→}
411→
412→func (s *Server) initializeCollectionSftp(cfg *sftp_impl.Config) error {
413→ var err error
414→ if cfg == nil {
415→ return errors.New("nil collection SFTP config")
416→ }
417→ if cfg.Address == "" {
418→ return errors.New("empty SFTP host url")
419→ }
420→ if cfg.UserCredentials == nil {
421→ return errors.New("nil cfg.UserCredentials")
422→ }
423→ cfg.UserCredentials, err = secret.ResolveSecretVal(context.Background(), cfg.UserCredentials, s.secretProvider)
424→ if err != nil {
425→ return err
426→ }
427→ s.collectionSFTPClient, err = sftp_impl.NewSFTPHandler(
428→ cfg,
429→ sftp_impl.WithSFTPClientInit(sftp_impl.InitSFTPClientPasswordAuth),
430→ sftp_impl.WithLogger(s.logger),
431→ sftp_impl.WithStats(s.stats),
432→ )
433→ return err
434→}
435→
436→func (s *Server) initializeThirdPartyCollectionSFTP(cfg *sftp_impl.Config) error {
437→ var err error
438→ if cfg == nil {
439→ return errors.New("nil 3rd party collection SFTP config")
440→ }
441→ if cfg.Address == "" {
442→ return errors.New("empty SFTP host url for 3rd party collection")
443→ }
444→ if cfg.UserCredentials == nil {
445→ return errors.New("nil cfg.UserCredentials for 3rd party collection")
446→ }
447→ cfg.UserCredentials, err = secret.ResolveSecretVal(context.Background(), cfg.UserCredentials, s.secretProvider)
448→ if err != nil {
449→ return err
450→ }
451→ s.thirdPartyCollectionSFTPClient, err = sftp_impl.NewSFTPHandler(
452→ cfg,
453→ sftp_impl.WithSFTPClientInit(sftp_impl.Init3rdPartyCollectionSFTPClientPasswordAuth),
454→ sftp_impl.WithLogger(s.logger),
455→ sftp_impl.WithStats(s.stats),
456→ )
457→ return err
458→}
459→
460→func (s *Server) initializeThirdPartyGpgHandler(cfg *gpg_impl.GPGSecretsConfig) error {
461→ if cfg == nil {
462→ return errors.New("no gpg config")
463→ }
464→
465→ resolvedGPGSecrets, err := secret.ResolveSecretVal(context.Background(), cfg.Secrets, s.secretProvider)
466→ if err != nil {
467→ return err
468→ }
469→ cfg.Secrets = resolvedGPGSecrets
470→ gpgHandler, err := gpg_impl.NewGPGHandler(cfg, gpg_impl.WithLogger(s.logger), gpg_impl.WithStats(s.stats))
471→ if err != nil {
472→ return err
473→ }
474→ s.thirdPartyCollectionGPGClient = gpgHandler
475→ return nil
476→}
477→
478→func (s *Server) initializeLockProvider(config *iredis.Config) error {
479→ lockProvider, err := distlock_impl.NewDistributedLockAPI(
480→ config,
481→ distlock_impl.WithLogger(s.logger),
482→ distlock_impl.WithStats(s.stats),
483→ )
484→ if err != nil {
485→ return err
486→ }
487→ s.lockProvider = lockProvider
488→ return nil
489→}
490→
491→func (s *Server) initializeQLDBClient(config *config.Config) error {
492→ client, err := qldb.NewQLDBProvider(config.QLDBConfig, qldb.WithLogger(s.logger), qldb.WithStats(s.stats),
493→ qldb.WithHealthCheckDisabled())
494→ if err != nil {
495→ return err
496→ }
497→ s.qldbClient = client
498→
499→ return nil
500→}
501→
502→func (s *Server) initializeDualTransactionProvider(config *config.Config) error {
503→ client, err := qldb.NewQLDBProvider(config.QLDBConfig, qldb.WithLogger(s.logger), qldb.WithStats(s.stats),
504→ qldb.WithHealthCheckDisabled())
505→ if err != nil {
506→ return err
507→ }
508→
509→ provider := qldb_migration.NewDualProvider(client, s.sqlClient, s.logger, s.stats)
510→
511→ s.DualTransactionProvider = provider
512→ return nil
513→}
514→
515→func (s *Server) initializeDAOClient() error {
516→ daoer, err := sqldao.NewDao(sqldao.WithLogger(s.logger),
517→ sqldao.WithStats(s.stats),
518→ sqldao.WithTimeProvider(s.TimeProvider),
519→ sqldao.WithSQL(s.sqlClient),
520→ sqldao.WithCreditReplaySQL(s.CreditReplaySQLClient),
521→ sqldao.WithBankingSQL(s.bankingSQLClient),
522→ sqldao.WithProducers(s.streamProducers),
523→ sqldao.WithUUIDGenerator(s.uuidGen))
524→ if err != nil {
525→ return err
526→ }
527→ s.sqlDao = daoer
528→
529→ consumerInfoAccessor, err := consumerinfoaccess.NewConsumerInfoAccessor(
530→ consumerinfoaccess.WithLogger(s.logger),
531→ consumerinfoaccess.WithStats(s.stats),
532→ consumerinfoaccess.WithConsumerProfileDao(daoer.ConsumerProfileDAO),
533→ consumerinfoaccess.WithProductAccountDao(daoer.ProductAccountDAO),
534→ consumerinfoaccess.WithApplicationDao(daoer.ApplicationDAO),
535→ consumerinfoaccess.WithCardDao(daoer.CardDAO))
536→ if err != nil {
537→ return err
538→ }
539→ s.ConsumerInfoAccessor = consumerInfoAccessor
540→
541→ offlineDaoer, err := sqldao.NewDao(sqldao.WithLogger(s.logger),
542→ sqldao.WithStats(s.stats),
543→ sqldao.WithTimeProvider(s.TimeProvider),
544→ sqldao.WithSQL(s.OfflineSqlClient),
545→ sqldao.WithBankingSQL(s.bankingSQLClient),
546→ sqldao.WithProducers(s.streamProducers),
547→ sqldao.WithUUIDGenerator(s.uuidGen))
548→ if err != nil {
549→ return err
550→ }
551→ s.OfflineSqlDao = offlineDaoer
552→
553→ return nil
554→}
555→
556→func (s *Server) initializeQueryers() error {
557→ queryers, err := qldbdao.NewQueryers(qldbdao.WithLogger(s.logger), qldbdao.WithStats(s.stats))
558→ if err != nil {
559→ return err
560→ }
561→ s.qldbQueryers = queryers
562→ return nil
563→}
564→
565→func (s *Server) initializeBalanceCalculator() error {
566→ calculatorAPI, err := balancecalculator.NewAPI(
567→ balancecalculator.WithLogger(s.logger),
568→ balancecalculator.WithStats(s.stats),
569→ balancecalculator.WithFeatureStore(s.FeatureStore),
570→ )
571→ if err != nil {
572→ return err
573→ }
574→ s.balanceCalculator = calculatorAPI
575→ return nil
576→}
577→
578→func (s *Server) initializeCreditCalculator() error {
579→ calculatorAPI, err := creditcalculator.NewCreditCalculator(
580→ creditcalculator.WithLogger(s.logger),
581→ creditcalculator.WithStats(s.stats),
582→ creditcalculator.WithFeatureStore(s.FeatureStore),
583→ )
584→ if err != nil {
585→ return err
586→ }
587→ s.CreditCalculator = calculatorAPI
588→ return nil
589→}
590→
591→func (s *Server) initializeCreditReplayOrchestrator() error {
592→ // init models first
593→ modelV1p0p1, err := replayv1.NewModelV1p0p1(
594→ replayv1.WithLogger(s.logger),
595→ replayv1.WithStats(s.stats),
596→ replayv1.WithFeatureStore(s.FeatureStore),
597→ replayv1.WithTimeProvider(s.TimeProvider),
598→ replayv1.WithLedgerUUIDGenerator(s.ledgerUUIDGen),
599→ replayv1.WithTransactionDAO(s.sqlDao.TransactionDAO),
<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>
1→package server
2→
3→import (
4→ "context"
5→ "errors"
6→ "time"
7→
8→ "github.com/opentracing/opentracing-go"
9→ "google.golang.org/protobuf/types/known/timestamppb"
10→
11→ "github.com/go-backend/banking/api/grpc/pb"
12→ census_grpc_v1 "github.com/go-backend/census/api/grpc/v1"
13→ gavel_api_v1 "github.com/go-backend/gavel/api/grpc/v1"
14→ "github.com/go-backend/internal/commons/aws/ddb"
15→ "github.com/go-backend/internal/commons/aws/s3"
16→ "github.com/go-backend/internal/commons/aws/secret"
17→ "github.com/go-backend/internal/commons/data/aurorasql"
18→ iredis "github.com/go-backend/internal/commons/data/redis"
19→ "github.com/go-backend/internal/commons/data/sqs/taskqueue"
20→ distlock_impl "github.com/go-backend/internal/commons/distlock/impl"
21→ "github.com/go-backend/internal/commons/dynval"
22→ "github.com/go-backend/internal/commons/envprovider"
23→ "github.com/go-backend/internal/commons/google/sheets"
24→ "github.com/go-backend/internal/commons/gpg"
25→ "github.com/go-backend/internal/commons/imprintuuid"
26→ consensus "github.com/go-backend/internal/commons/jobrunner/consensus/impl"
27→ "github.com/go-backend/internal/commons/kitservice/imprinterrors"
28→ "github.com/go-backend/internal/commons/observability/gometrics"
29→ "github.com/go-backend/internal/commons/observability/logging"
30→ "github.com/go-backend/internal/commons/observability/stats"
31→ "github.com/go-backend/internal/commons/observability/tracing"
32→ "github.com/go-backend/internal/commons/runners"
33→ "github.com/go-backend/internal/commons/sftp"
34→ timeprovider "github.com/go-backend/internal/commons/timeprovider/v2"
35→ "github.com/go-backend/internal/commons/util/contextutil"
36→ imprint_temporal_client "github.com/go-backend/internal/components/clients/temporal"
37→ productconfig "github.com/go-backend/internal/components/ddbentities/productconfig/impl"
38→ "github.com/go-backend/internal/components/domains/contentregistry"
39→ "github.com/go-backend/internal/components/domains/pdf"
40→ "github.com/go-backend/ledger/api/balancecalculator"
41→ ledger_grpc_v1 "github.com/go-backend/ledger/api/grpc/pb"
42→ "github.com/go-backend/ledger/internal/domains/collectionmanager"
43→ creditlineoverridemanager "github.com/go-backend/ledger/internal/domains/creditlineoverridemanager/impl"
44→ "github.com/go-backend/ledger/internal/domains/creditmanager"
45→ "github.com/go-backend/ledger/internal/domains/creditprofilemanager"
46→ criteriamodelmgr "github.com/go-backend/ledger/internal/domains/criteriamodelmgr"
47→ delinquencymanager "github.com/go-backend/ledger/internal/domains/delinquencymanager/impl"
48→ "github.com/go-backend/ledger/internal/domains/feeprofilemanager"
49→ "github.com/go-backend/ledger/internal/domains/loanmanager"
50→ "github.com/go-backend/ledger/internal/domains/loantapemanager"
51→ "github.com/go-backend/ledger/internal/domains/qldbcreditledger"
52→ rebalancemanager "github.com/go-backend/ledger/internal/domains/rebalancemanager/impl"
53→ "github.com/go-backend/ledger/internal/domains/reconciliationmanager"
54→ sqlledger "github.com/go-backend/ledger/internal/domains/sqlledger/impl"
55→ "github.com/go-backend/ledger/internal/domains/term_actions"
56→ "github.com/go-backend/ledger/internal/domains/transaction_groups"
57→ "github.com/go-backend/ledger/internal/handlers/collection/consumerinfoaccess"
58→ "github.com/go-backend/ledger/internal/handlers/customer_term_action"
59→ "github.com/go-backend/ledger/internal/handlers/job"
60→ "github.com/go-backend/ledger/internal/handlers/statement"
61→ "github.com/go-backend/ledger/internal/handlers/transaction"
62→ "github.com/go-backend/ledger/internal/handlers/transaction_group"
63→ "github.com/go-backend/ledger/internal/internal/creditcalculator"
64→ "github.com/go-backend/ledger/internal/internal/creditreplay/orchestrator"
65→ "github.com/go-backend/ledger/internal/internal/txvalidator"
66→ "github.com/go-backend/ledger/internal/streams"
67→ ledger_uuid "github.com/go-backend/ledger/internal/uuid"
68→ crate_term_templates "github.com/go-backend/ledger/partner/crate/term_templates"
69→ "github.com/go-backend/ledger/pkg/config"
70→ "github.com/go-backend/ledger/pkg/dao/qldbdao"
71→ "github.com/go-backend/ledger/pkg/dao/sqldao"
72→ ledger_date_finder "github.com/go-backend/ledger/pkg/date"
73→ "github.com/go-backend/ledger/pkg/dto"
74→ "github.com/go-backend/ledger/qldb_migration"
75→ messageservice "github.com/go-backend/messenger/pkg/service"
76→ scribe_api "github.com/go-backend/scribe/proto/api/v1"
77→)
78→
79→var _ ledger_grpc_v1.LedgerServer = (*Server)(nil)
80→
81→// Server is the Ledger Server Implementation
82→type Server struct {
83→ ledger_grpc_v1.UnsafeLedgerServer
84→
85→ // basic
86→ logger logging.API
87→ stats *stats.Client
88→ uuidGen imprintuuid.Generator
89→ statementDateFinder ledger_date_finder.StatementDateFinder
90→ ledgerUUIDGen ledger_uuid.LedgerUUIDGenerator
91→ TimeProvider timeprovider.TimeProvider
92→ envProvider envprovider.EnvProvider
93→ secretProvider secret.SecretProvider
94→ streamProducers *streams.Producers
95→ FeatureStore dynval.FeatureStore
96→ metricsDaemon *gometrics.Daemon
97→ ddbProvider ddb.Provider
98→ contentRegistry contentregistry.ContentRegistry
99→ redisClient iredis.Client
100→
101→ // clients
102→ messenger messageservice.MessengerService
103→ census census_grpc_v1.CensusClient
104→ scribe scribe_api.PDFClient
105→ gavel gavel_api_v1.GavelClient
106→ banking pb.BankingClient
107→ transactionTaskQueue taskqueue.TaskQueue
108→ transactionDlqDriver taskqueue.TaskQueue
109→ centralizedWorkflowTaskQueue taskqueue.TaskQueue
110→ centralizedWorkflowDlqDriver taskqueue.TaskQueue
111→ statementTaskQueue taskqueue.TaskQueue
112→ statementDLQDriver taskqueue.TaskQueue
113→ balanceCalculator *balancecalculator.API
114→ CreditCalculator *creditcalculator.CalculatorImpl
115→ creditReplayOrchestrator *orchestrator.CreditReplayOrchestrator
116→ txValidator *txvalidator.API
117→ sqlClient *aurorasql.AuroraSQLClient
118→ OfflineSqlClient *aurorasql.AuroraSQLClient
119→ bankingSQLClient *aurorasql.AuroraSQLClient
120→ CreditReplaySQLClient *aurorasql.AuroraSQLClient
121→ qldbClient qldbClient
122→ sqlDao *sqldao.Dao
123→ OfflineSqlDao *sqldao.Dao
124→ qldbQueryers *qldbdao.Queryers
125→ s3Client s3.Client
126→ febSftpClient sftp.Client
127→ sheetsService *sheets.Service
128→ collectionSFTPClient sftp.Client
129→ thirdPartyCollectionSFTPClient sftp.Client
130→ thirdPartyCollectionGPGClient gpg.Handler
131→ ConsumerInfoAccessor *consumerinfoaccess.Accessor
132→ productConfigAccessor *productconfig.Accessor
133→ ConsensusClient *consensus.ConsensusClientImpl
134→ DualTransactionProvider qldb_migration.DualTransactionProvider
135→ temporalClient imprint_temporal_client.Client
136→
137→ lockProvider *distlock_impl.API
138→
139→ // domains
140→ pdfManager *pdf.API
141→ qldbCreditLedgerManager *qldbcreditledger.Manager
142→ SQLLedgerManager *sqlledger.Manager
143→ CreditProfileManager creditprofilemanager.CreditProfile
144→ FeeProfileManager *feeprofilemanager.Manager
145→ creditManager *creditmanager.Manager
146→ delinquencyManager *delinquencymanager.Manager
147→ creditLineOverrideManager *creditlineoverridemanager.Manager
148→ reconciliationManager *reconciliationmanager.Manager
149→ loanTapeManager *loantapemanager.Manager
150→ loanManager *loanmanager.Manager
<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>
Now I have a good understanding of the current patterns. Let me also look at the InitDomains function to see how domains are wired up:
{
"pattern": "func InitDomains",
"path": "/Users/willlarson/go-backend/ledger/internal/server",
"output_mode": "content",
"-A": 100
}ledger/internal/server/server_init.go:987:func InitDomains(ctx context.Context, s *Server, config *config.Config) error {
ledger/internal/server/server_init.go-988- if err := s.initializeContentRegistry(config.ContentConfigStore); err != nil {
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-989- return err
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-990- }
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-991-
ledger/internal/server/server_init.go-992- if err := s.initializeCriteriaModelManager(); err != nil {
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-993- return err
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-994- }
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-995-
ledger/internal/server/server_init.go-996- criteriaHandlerImpl, err := criteria_impl.NewHandler(
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-997- s.logger,
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-998- s.stats,
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-999- s.TimeProvider,
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1000- s.uuidGen,
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1001- s.criteriaModelMgr,
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1002- s.sqlDao.CriteriaDAO,
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1003- s.sqlDao.TermsDAO,
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1004- )
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1005- if err != nil {
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1006- return err
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1007- }
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1008- s.criteriaHandler = criteriaHandlerImpl
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1009-
ledger/internal/server/server_init.go-1010- if err := s.initializeQldbCreditLedger(); err != nil {
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1011- return err
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1012- }
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1013-
ledger/internal/server/server_init.go-1014- if err := s.initializeSqlLedger(); err != nil {
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1015- return err
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1016- }
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1017-
ledger/internal/server/server_init.go-1018- if err := s.initializeCreditProfileManager(); err != nil {
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1019- return err
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1020- }
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1021-
ledger/internal/server/server_init.go-1022- if err := s.initializeFeeProfileManager(); err != nil {
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1023- return err
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1024- }
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1025-
ledger/internal/server/server_init.go-1026- if err := s.initializeDelinquencyManager(); err != nil {
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1027- return err
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1028- }
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1029-
ledger/internal/server/server_init.go-1030- if err := s.initializeCreditLineOverrideManager(); err != nil {
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1031- return err
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1032- }
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1033-
ledger/internal/server/server_init.go-1034- if err := s.initializeReconciliationManager(); err != nil {
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1035- return err
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1036- }
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1037-
ledger/internal/server/server_init.go-1038- if err := s.initializeLoanTapeManager(); err != nil {
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1039- return err
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1040- }
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1041-
ledger/internal/server/server_init.go-1042- if err := s.initializePdfManager(); err != nil {
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1043- return err
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1044- }
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1045-
ledger/internal/server/server_init.go-1046- if err := s.initializeCollectionManager(); err != nil {
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1047- return err
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1048- }
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1049-
ledger/internal/server/server_init.go-1050- if err := s.initializeTransactionGroupManager(); err != nil {
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1051- return err
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1052- }
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1053-
ledger/internal/server/server_init.go-1054- if err := s.initializeTermActionManager(); err != nil {
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1055- return err
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1056- }
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1057-
ledger/internal/server/server_init.go-1058- if err := s.initializeCreditReplayOrchestrator(); err != nil {
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1059- return err
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1060- }
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1061-
ledger/internal/server/server_init.go-1062- if err := s.initializeCreditManager(); err != nil {
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1063- return err
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1064- }
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1065-
ledger/internal/server/server_init.go-1066- if err := s.initializeLoanManager(); err != nil {
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1067- return err
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1068- }
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1069-
ledger/internal/server/server_init.go-1070- if err := s.initializeRebalanceManager(); err != nil {
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1071- return err
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1072- }
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1073-
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1074- return nil
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1075-}
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1076-
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1077-func (s *Server) initializeCriteriaModelManager() error {
ledger/internal/server/server_init.go-1078- criteriaModelManager, err := criteriamodelmgr.NewManager(
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1079- criteriamodelmgr.WithLogger(s.logger),
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1080- criteriamodelmgr.WithStats(s.stats),
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1081- criteriamodelmgr.WithCriteriaDAO(s.sqlDao.CriteriaDAO),
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1082- criteriamodelmgr.WithTermsDAO(s.sqlDao.TermsDAO),
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1083- criteriamodelmgr.WithTransactionTermMatchProducer(s.streamProducers.EntityTransactionTermMatchStream),
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1084- )
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1085- if err != nil {
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1086- return err
/Users/willlarson/go-backend/ledger/internal/server/server_init.go-1087- }./gavel/internal/vendors/clients/novacredit/mocks/z_mock_nova_credit_client.go ./gavel/internal/vendors/webhooks/persona/mocks/z_mock_persona_handler.go ./gavel/internal/vendors/webhooks/cashatlas/mocks/z_mock_cash_atlas_handler.go ./gavel/internal/dao/loanapplication_nonce/mocks/z_mock_nonce_dao.go ./gavel/internal/dao/loanapplication_nonce/mocks/z_mock_mysql_test.go ./gavel/internal/dao/loanpaymentplan/mocks/z_mocks_loan_payment_plan.go ./gavel/internal/dao/allowlist/mocks/z_mocks_allowlist.go ./gavel/internal/dao/allowlist/mocks/z_mocks_application_allowlist.go ./gavel/internal/dao/loanapplication/mocks/z_mocks_loan_application.go ./gavel/internal/dao/loanapplication/dao/encryption/mocks/z_mock_loanapp_encryption.go ./gavel/internal/dao/basedao/mocks/z_mocks_mysql.go ./gavel/internal/dao/underwriting/mocks/z_mocks_loan_risk_decision_dao.go ./gavel/internal/feature/mocks/z_mocks_store.go ./gavel/internal/domain/loanpaymentplan/mocks/z_mock_loanpaymentplan_manager.go ./gavel/internal/domain/loanapplication/mocks/z_mock_application_manager.go ./gavel/internal/domain/underwriting/mocks/z_mock_underwriting_manager.go ./gavel/internal/handlers/applicationorchestrator/mocks/z_mock_application_orchestrator.go ./gavel/internal/handlers/payment_plans/mocks/z_mock_payment_plans_handler.go ./gavel/internal/handlers/allowlist/mocks/z_mock_allowlist.go ./gavel/api/grpc/v1/z_mocks_gavel_client.go
Now I have a comprehensive picture. Let me look at how mocks are generated and how they're used.
cat .gitattributes 2>/dev/null || echo "No .gitattributes file"
// Code generated by mockery v2.53.5. DO NOT EDIT.
package feature_mocks
import (
context "context"
mock "github.com/stretchr/testify/mock"
)
// Store is an autogenerated mock type for the Store type
type Store struct {
mock.Mock
}
// GetKYCWorkflowToken provides a mock function with given fields: ctx
func (_m *Store) GetKYCWorkflowToken(ctx context.Context) string {
ret := _m.Called(ctx)
if len(ret) == 0 {
panic("no return value specified for GetKYCWorkflowToken")
}
var r0 string
if rf, ok := ret.Get(0).(func(context.Context) string); ok {
r0 = rf(ctx)
} else {
r0 = ret.Get(0).(string)
}Now I have all the information I need. Let me write up concrete examples for:
Let me provide really concrete examples.
Now I have the full picture. Here are concrete examples:
Every time someone adds a new domain manager to ledger, they must:
server.go (line ~140-170)initializeXxxManager() method to server_init.goInitDomains() (line ~987-1075)This means 3 locations in 2 files that everyone editing domains must touch. When two people add domains in parallel → merge conflict.
func InitDomains(ctx context.Context, s *Server, config *config.Config) error {
// ... 20+ other initializations ...
if err := s.initializeDelinquencyManager(); err != nil {
return err
}
if err := s.initializeCreditLineOverrideManager(); err != nil {
return err
}
// ... 10+ more initializations ...
}
Every new domain adds 3 lines here. Two PRs adding domains = guaranteed conflict.
Step 1: Create a registry (new file: ledger/internal/server/registry.go)
package server
import "context"
// DomainInitializer is a function that initializes a domain and attaches it to the server.
type DomainInitializer struct {
Name string
Init func(ctx context.Context, s *Server) error
}
// domainRegistry holds all registered domain initializers.
// Domains register themselves via init() functions.
var domainRegistry []DomainInitializer
// RegisterDomain adds a domain initializer to the registry.
// Called from init() functions in each domain package.
func RegisterDomain(name string, init func(ctx context.Context, s *Server) error) {
domainRegistry = append(domainRegistry, DomainInitializer{Name: name, Init: init})
}
// InitAllDomains runs all registered domain initializers.
func InitAllDomains(ctx context.Context, s *Server) error {
for _, d := range domainRegistry {
if err := d.Init(ctx, s); err != nil {
return fmt.Errorf("failed to initialize domain %s: %w", d.Name, err)
}
}
return nil
}
Step 2: Each domain registers itself (e.g., ledger/internal/domains/delinquencymanager/register.go)
package delinquencymanager
import (
"context"
"github.com/go-backend/ledger/internal/server"
)
func init() {
server.RegisterDomain("delinquency", initDelinquencyManager)
}
func initDelinquencyManager(ctx context.Context, s *server.Server) error {
manager, err := NewManager(
WithLogger(s.Logger()),
WithStats(s.Stats()),
WithCreditLineDAO(s.SQLDao().CreditLineDAO),
WithTermsDAO(s.SQLDao().TermsDAO),
WithFeatureStore(s.FeatureStore()),
)
if err != nil {
return err
}
s.SetDelinquencyManager(manager)
return nil
}
Step 3: Replace InitDomains with the registry call
// server_init.go - InitDomains becomes:
func InitDomains(ctx context.Context, s *Server, config *config.Config) error {
return InitAllDomains(ctx, s)
}
Step 4: Import all domain packages in one place (ledger/internal/server/domains.go)
package server
// Import all domain packages to trigger their init() registration.
// Add new domains here - this is the ONLY file that needs editing.
import (
_ "github.com/go-backend/ledger/internal/domains/collectionmanager"
_ "github.com/go-backend/ledger/internal/domains/creditlineoverridemanager"
_ "github.com/go-backend/ledger/internal/domains/creditmanager"
_ "github.com/go-backend/ledger/internal/domains/creditprofilemanager"
_ "github.com/go-backend/ledger/internal/domains/delinquencymanager"
_ "github.com/go-backend/ledger/internal/domains/feeprofilemanager"
_ "github.com/go-backend/ledger/internal/domains/loanmanager"
// ... etc
)
| Aspect | Before | After |
|---|---|---|
| Files to edit for new domain | 2 (server.go, server_init.go) |
1 (domains.go import + new register.go) |
| Lines changed in shared files | ~15 lines across 2 files | 1 import line |
| Merge conflict probability | High (sequential blocks) | Low (import list is easy to merge) |
| Domain code locality | Split across 3 locations | All in domain package |
The key insight: import lists merge cleanly because git can auto-merge additions to different lines. Sequential if err := ...; return err blocks cannot.
Generated files like z_mocks_*.go and z_swagger*.json are:
- Changed in 15+ commits out of the last 50
- Regenerated whenever their source changes
- Cause merge conflicts that are meaningless (just regenerate!)
Example: Two PRs both add a method to census.proto. Both regenerate z_mocks_census_grpc_client.go. Merge conflict. But the resolution is always: regenerate from merged proto.
.gitattributes Merge StrategyCreate .gitattributes in repo root:
# Generated mock files - always accept "ours" on merge, then regenerate
**/z_mock*.go merge=ours
**/z_mocks_*.go merge=ours
# Generated swagger files - same strategy
api/swagger/z_swagger*.json merge=ours
api/swagger/z_swagger*.yaml merge=ours
# Generated proto files
**/*.pb.go merge=ours
How merge=ours works:
When git encounters a conflict in these files during merge/rebase: 1. Instead of showing conflict markers, it keeps "our" version 2. The merge succeeds without manual intervention 3. CI then regenerates the files from source (proto, interfaces)
Required CI step (add to PR workflow):
# .github/workflows/pr-checks.yml
- name: Regenerate and verify generated files
run: |
# Regenerate mocks
go generate ./...
# Regenerate swagger
./scripts/generate-and-validate-swagger.sh
# Fail if there are uncommitted changes (generated files out of sync)
git diff --exit-code || (echo "Generated files out of sync. Run 'go generate ./...' and commit." && exit 1)
A more aggressive approach: add generated files to .gitignore and generate them in CI/locally.
.gitignore additions:
# Generated mocks - regenerate with 'go generate ./...'
**/z_mock*.go
**/z_mocks_*.go
# Generated swagger - regenerate with './scripts/generate-and-validate-swagger.sh'
api/swagger/z_swagger*.json
api/swagger/z_swagger*.yaml
Makefile target for local dev:
.PHONY: generate
generate:
go generate ./...
./scripts/generate-and-validate-swagger.sh
.PHONY: setup
setup: generate
# Other setup steps...
| Approach | Merge Conflicts | Repo Size | Dev Experience |
|---|---|---|---|
| Current (commit all) | Frequent | Larger (+1003 mock files) | Simple but conflict-prone |
merge=ours + CI check |
None | Same | Auto-resolves, CI validates |
.gitignore generated |
None | Smaller | Must run make generate |
Recommendation: Start with merge=ours + CI validation. It's less disruptive than removing files from git entirely, but eliminates the merge conflict problem.
Before (typical merge conflict in z_mocks_census_grpc_client.go):
<<<<<<< HEAD
func (_m *MockCensusClient) ListCreditLinesByDebtSale(ctx context.Context, ...) {
=======
func (_m *MockCensusClient) BulkUploadDebtSaleCreditLine(ctx context.Context, ...) {
>>>>>>> feature-branch
Developer must: understand both changes, manually merge, hope they got it right.
After (with merge=ours):
# Merge succeeds automatically
# CI runs go generate
# CI commits regenerated file (or fails if source is wrong)
No manual intervention. The generated file is always correct because it's regenerated from the merged source.
.gitattributes for generated filesz_* files disappearregistry.goInitDomains working for non-migrated domainscensus, onboard, teller