The Dependency Inversion Principle (D in SOLID) has two parts: high-level modules (business rules) must not depend on low-level modules (SMTP, databases, files, the clock); both must depend on abstractions. And abstractions must not depend on details: the details depend on abstractions.

Left: the service depends on EmailSender. Right: the service and the notifiers depend on the Notifier protocol
Left: the service depends on EmailSender. Right: the service and the notifiers depend on the Notifier protocol

The violation: a hard-wired detail

OrderService creates EmailSender() in its constructor. The business rule now knows the delivery channel. Sending an SMS means editing the service, and a test can only run by sending a real email.

python
class EmailSender:
    def send(self, to, text):
        print(f"EMAIL to {to}: {text}")

class OrderService:
    def __init__(self):
        self.sender = EmailSender()      # hard-wired detail

    def place(self, user, item):
        self.sender.send(user, f"order {item} accepted")

OrderService().place("ann@x.io", "book")
text
EMAIL to ann@x.io: order book accepted

The fix: an abstraction owned by the client