Good Code
The good version names the one behavior the service needs, so production clients and tests can provide the smallest useful implementation.
Lesson 05
Define small interfaces where behavior is consumed instead of forcing every producer to satisfy a large package-wide contract.
type Mailer interface {
Send(ctx context.Context, message Message) error
}
type Service struct {
mailer Mailer
}
func (s *Service) RequestReview(ctx context.Context, review Review) error {
return s.mailer.Send(ctx, Message{
To: review.ReviewerEmail,
Subject: "Review requested",
})
}type NotificationClient interface {
SendEmail(ctx context.Context, to string, subject string, body string) error
SendSMS(ctx context.Context, phone string, body string) error
SendSlack(ctx context.Context, channel string, body string) error
Close() error
}
func RequestReview(ctx context.Context, client NotificationClient, review Review) error {
return client.SendEmail(ctx, review.ReviewerEmail, "Review requested", "")
}The good version names the one behavior the service needs, so production clients and tests can provide the smallest useful implementation.
The bad version makes a caller depend on email, SMS, Slack, and lifecycle methods even though it only sends one email.