0% прочитано

SOLID. Principle 4 - Interface Segregation Principle in Practice in a Real Laravel Project

Using real ContentBlock and Analytics examples from ICanUp, I explain Interface Segregation Principle: a class should not depend on methods or capabilities it does not need.

23 вересня 2026 р. 8 хв читанняLaravel

In ICanUp, different ContentBlocks have different capabilities. Paragraph needs basic data and validation rules. Form or Hero blocks have presentation options. Tips Explorer is additionally restricted to a particular blockable type.

If all of those capabilities were placed into one large interface, every block would be forced to implement methods it does not need.

Interface Segregation Principle

Interface Segregation Principle: Clients should not be forced to depend on methods they do not use.

In simpler terms: do not force a class to support capabilities it does not need. Keep the base contract focused and move optional capabilities into separate interfaces.

Example 1 - A Base Contract and a Separate Capability

PHP - ContentBlock base and scoped contracts
interface ContentBlockDefinitionInterface{    public function type(): ContentBlockTypeEnum;     public function label(): string;     public function defaultData(): array;     public function rules(): array;     public function attributes(): array;} interface ScopedContentBlockDefinitionInterface{    public function supportsBlockable(

ContentBlockDefinitionInterface contains only what every definition needs: type, label, default data, rules, and attributes.

TipsExplorerBlockDefinition has one extra constraint - it works only for page. That is why supportsBlockable() lives in a separate ScopedContentBlockDefinitionInterface instead of the base contract.

ContentBlockRegistry checks that capability only when it exists. Paragraph, Image, or Quote do not need to know about scope at all.

Example 2 - Presentation Methods Are Not Needed by Every Block

PHP - PresentationBlockDefinitionInterface
interface PresentationBlockDefinitionInterface    extends ContentBlockDefinitionInterface{    public function contentRules(): array;     public function configurationRules(): array;     public function variants(): array;     public function options(): array;     public function makeData(        array $data,    ): BlockDataInterface;} 

Presentation blocks need additional methods: variants(), options(), separate content/configuration rules, and makeData().

A regular ParagraphBlockDefinition does not need them. It implements only the base contract and does not return empty variants or fake configuration merely because other blocks require those features.

This is the simple effect of ISP: a simple implementation can remain simple.

Example 3 - Analytics Separates Writing and Reading

PHP - Separate Analytics read and write contracts
interface AnalyticsHistoryRepositoryInterface{    public function record(        AnalyticsMeasurement $measurement,    ): void;} interface AnalyticsAggregationRepositoryInterface{    public function aggregate(        AnalyticsAggregationQuery $query,    ): iterable;} final readonly class AnalyticsTracker{

Analytics also avoids one large repository interface for every operation.

AnalyticsTracker writes measurements, so it depends on AnalyticsHistoryRepositoryInterface with record(). It does not need aggregate().

ContentAnalyticsQuery reads aggregated data, so it depends on AnalyticsAggregationRepositoryInterface. It does not need record().

Each client sees only the part of the API it actually uses.

ISP Is Not Just About Small Interfaces

An interface with one method is not automatically good, and an interface with five methods is not automatically bad.

The main question is: do all clients of this contract genuinely need these methods?

If a class constantly returns empty values, throws NotSupportedException, or implements a method only because the interface requires it, the contract boundary deserves another look.

Practical Takeaway

My simplest formulation of Interface Segregation Principle is: a class should not depend on capabilities it does not need.

In ContentBlocks, the base interface contains only shared behavior while scope and presentation live in separate capabilities. In Analytics, writing and reading have separate contracts.

The result is that interfaces describe real code needs instead of every capability in the system at once.