You still need virtual dispatch (or type pattern matching) if there is a variable which can contain either a BDRepo or a LoggedRepo(BDRepo).
module HttpHandler (R : Repo) : sig
val handle_user : R.t -> HttpWriter.t -> username:string -> unit
end = struct
let handle_user repo writer ~username =
match R.get_user repo username with
| None -> Writer.write_status writer 404
| Some user -> Writer.write_body writer (User.to_string user)
end
and then to construct a handler module with a proper repo.