Skip to content

Instantly share code, notes, and snippets.

@rahulbir
Last active December 8, 2020 19:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rahulbir/cbf24c3c65a59f1fc45e078c364a7493 to your computer and use it in GitHub Desktop.
Save rahulbir/cbf24c3c65a59f1fc45e078c364a7493 to your computer and use it in GitHub Desktop.
Pseudo code for refactoring appointment_routes
//api-export-action /the/route/here/cancel
func Cancel(request WebRequest) (WebResponse, error){
validateRequest()
// AppointmentRepository is injected here so that we can test CancelConsumerAction by stubbing the interface
result, err := CancelConsumerAction(mapFromWebRequest(request), AppointmentRepository{})
if err return err
return mapToWebResponse(result), nil
}
// repository interface, defines functions that determine how to obtain
// data from different sources like database, salesforce, other external services
type AppointmentRepository interface {
Fetch(appt.ID) LogisticAppointment // Shorten to `fetch` probably
Save(appt) error
List() []*LogisticAppointment
}
type CancelInput struct {
ID
Reason
}
type CancelResult struct {
ID
}
// In some cases these functions could be shared between ops/ios/consumer/etc.
// This example will show the case where ops and consumer have different implementations
func (input *CancelInput) CancelConsumerAction(repository AppointmentRepository) (CancelResult, error) {
appt := repository.Fetch(input.ID)
logisticAppointment.Cancel(appt, input.Reason)
err := repository.Save(appt)
if err return err
// Depending on the action...might not need a Result object
return CancelResult{
id: appt.ID
}
}
func CancelOpsAction(input CancelInput) (CancelResult, error) {
appt := repository.fetchAppointment(input.ID)
logisticAppointment.Cancel(appt, input.Reason)
appt.canceledBy = input.Admin // extra functionality specific to ops
err := repository.Save(appt)
if err return err
// Depending on the action...might not need a Result object
return CancelResult{
id: appt.ID
}
}
// tests at the action logic level are much simpler to test now since we can stub out the repository
testCancelAction() {
stubAppt := {ID: "appt", status: "ACTIVE"}
stub := stubRepository{
appt: stubAppt
}
result, err := cancelAction(input.ID = "appt", reason = "test reason")
assert.Equal(result, whatever)
assert.Equal(stub.savedAppt, {status: CANCELLED, reason: "test reason"...})
}
stubRespository.Fetch() {
return this.appt
}
stubRespository.Save(appt) {
this.savedAppt = appt
}
// Our new public facing business model
type LogisticAppointment {
id
starttime
endtime
status
}
func logisticAppointment.Cancel(appt LogisticAppointment, reason string /*or bigger struct*/) LogisticAppointment {
appt.canceledAt = now()
appt.status = "CANCELLED"
appt.reason = reason
return appt
}
// This file implements the functions that the AppointmentRepository interface needs
AppointmentRepository.Fetch(id) {
appt := frunkTable.Get(id)
logisticsAppt := //map from current appointment model to LogisticsAppointment
return logisticsAppt
}
AppointmentRepository.Save(logisticsAppt LogisticsAppointment) {
appt := // map from LogAppt to frunky appt
frunkTable.UpdateWithID(appt)
}
AppointmentRepository.List() {
appts := frunkTable.List(appt)
logisticsAppt := //map from appointments model to LogisticsAppointments
return logisticsAppts
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment