The path is given by db_attach/2, e.g.
db_attach('data.journal', [])
To keep the example simple it used the same directory as the code, e.g.
?- working_directory(_, 'C:/Users/Eric/Documents/Projects/Prolog/Persistence example/').
true.
Since db_path/2 can take a full path, you could also do something like
db_attach('C:/Users/Eric/Documents/Projects/Prolog/Persistence example/data.journal', [])
@jan Can you check this answer to make sure I am not giving very bad advise.
I would not. That warning is because I still don’t fully know the correct way to use library(persistency).
This also goes back to the statement I made in the explanation.
When writing to a file that can be open by multiple processes at the same time, the use of with_mutex/2 is probably required. However
entity(A).
is just trying to read the file and so no lock should be needed and thus with_mutex/2 should not be needed.
Since the with_mutex/2 is not needed, the predicate to read the facts, entity/1 which is in the facts module can be exported as is and the wrapper predicate exists_entity/1 can be removed. The changes to all three source code files becomes
program_a.pl
:- use_module('program_database.pl').
program_1 :-
    format('Starting saving ~n',[]),
    A = "a",
    B = "b",
    % Stuff
    %assert(entity(A)),
    (
        % exists_entity(A)
        entity(A)
    ;
        add_entity(A)
    ),
    %assert(entity(B)),
    (
        % exists_entity(B)
        entity(B)
    ;
        add_entity(B)
    ),
    % More stuff
    format('Done ~n',[]).
program_b.pl
:- use_module('program_database.pl').
program_2 :-
    format('Starting reading program ~n',[]),
    % Stuff
    write_entities.
write_entities :-
    % facts:entity(A),
    % exists_entity(A),
    entity(A),
    write_canonical(A),nl,
    fail.
program_database.pl
:- module(
    facts,
    [
      add_entity/1,          % +Identifier:string
      % exists_entity/1        % ?Identifier:string
      entity/1               % ?Identifier:string
    ]
  ).
:- use_module(library(persistency)).
:- persistent
    entity(identifier:string).
% :- initialization(db_attach('C:/Users/Eric/Documents/Projects/Prolog/Persistence example/data.journal', [])).
:- initialization(db_attach('data.journal', [])).
% exists_entity(Identifier):-
%   with_mutex(facts_journal, entity(Identifier)).
add_entity(Identifier):-
  with_mutex(facts_journal, assert_entity(Identifier)).
In other words what library(persistency) does is, that you give it a spec and it creates the 4 predicates
name(Arg, ...)
assert_name(Arg, ...)
retract_name(Arg, ...)
retractall_name(Arg, ...)
and you give it a file name, e.g. db_attach('data.journal', []) and it stores the clauses created by using assert_name(Arg, ...) into memory while the programming is running and a file when the program ends. I have not worked with the predicates such as db_detach/0, db_sync/1 and db_sync_all/1 so I can’t say more about them.
The rest is just code I added to make it more useful. It does not even need to be put into a separate module. However in the several cases I used it for and the case you used it for, it makes more sense as a module.
So you have  library(persistency) working and know enough to now ask good questions, that pretty much puts you near to what I know about library(persistency), e.g. I only experimented for several hours when I asked the questions in Trying to understand library(persistency)
Thanks for asking the questions because it helps to reinforce/change my understanding of using library(persistency) and how others are using it and that some of the ways I explain it need more clarification. 