Questo probabilmente non è un problema. I nomi di tabelle e colonne in un database formano l'interfaccia di quel database. Non sono solo un dettaglio di implementazione arbitraria che cambierà per capriccio. Questo è simile al modo in cui i nomi di classi e metodi in un programma sono "hardcoded" nel codice chiamante. Questo non è hardcoding, è solo codice.
In un programma orientato agli oggetti, se ti aspetti che l'interfaccia di qualche classe che stai usando possa cambiare, allora puoi implementare un strato anticorruzione . Questa potrebbe essere una classe adattatore che specifica un'interfaccia stabile che si desidera utilizzare, ma internamente le chiamate dell'adattatore nell'interfaccia instabile, esistente.
Il pattern del repository è esattamente un adattatore, ma per le origini dati. Il repository espone una comoda interfaccia database-agnostica alla tua logica aziendale. Internamente, il repository potrebbe utilizzare SQL per caricare e archiviare i dati. Se il database cambia, non è necessario modificare tutto il codice ma solo il codice nell'adattatore: la modifica è stata concentrata in una piccola area. O potresti scrivere un adattatore completamente nuovo. Un cambiamento potrebbe essere che tu usi un database completamente diverso, o forse che tu aggiunga una colonna da qualche parte - si applicano gli stessi concetti.
Tuttavia è necessario che questo adattatore o repository sia a conoscenza dell'origine dati su cui si sta sottraendo. È possibile aggiungere un altro livello di astrazione tra il repository e l'origine dati, ma questo ha un valore decrescente. A un certo punto, deve essere effettuata la connessione tra il codice e i nomi delle tabelle nel database. Anche se lo si estrae in un file di configurazione, il file di configurazione è effettivamente codice che "codifica" questi nomi di tabelle.