What Are Service Contracts
Service contracts define stable API interfaces for module interactions.
Why service contracts matter:
- Stable API across versions
- Decoupled implementation
- Easy to test
- REST/GraphQL API generation
- Third-party integration
Service contract layers:
API Layer (Interfaces)
├── Data Interfaces (PostInterface)
├── Repository Interfaces (PostRepositoryInterface)
└── Service Interfaces (PostServiceInterface)
Implementation Layer
├── Models (Post)
├── Resource Models (Post)
├── Repositories (PostRepository)
└── Service Classes (PostService)
Module structure:
app/code/Vendor/Blog/
├── Api/
│ ├── Data/
│ │ ├── PostInterface.php
│ │ └── PostExtensionInterface.php
│ ├── PostRepositoryInterface.php
│ └── PostServiceInterface.php
├── Model/
│ ├── Post.php
│ ├── PostRepository.php
│ └── ResourceModel/
│ ├── Post.php
│ └── Post/Collection.php
└── etc/
├── module.xml
├── di.xml
└── webapi.xml
Data Interfaces
Data interfaces define the structure of data objects.
Data interface:
<?php
namespace Vendor\Blog\Api\Data;
interface PostInterface
{
const TITLE = 'title';
const CONTENT = 'content';
const STATUS = 'status';
const CREATED_AT = 'created_at';
const UPDATED_AT = 'updated_at';
public function getId(): ?int;
public function getTitle(): string;
public function setTitle(string $title): self;
public function getContent(): string;
public function setContent(string $content): self;
public function getStatus(): int;
public function setStatus(int $status): self;
public function getCreatedAt(): ?string;
public function setCreatedAt(string $createdAt): self;
public function getUpdatedAt(): ?string;
public function setUpdatedAt(string $updatedAt): self;
}
Data interface best practices:
// 1. Use constants for field names
const TITLE = 'title';
// 2. Return self for fluent interface
public function setTitle(string $title): self;
// 3. Use nullable for optional fields
public function getCreatedAt(): ?string;
// 4. Type-hint all parameters
public function setStatus(int $status): self;
// 5. Document with PHPDoc
/**
* Get post title
* @return string
*/
public function getTitle(): string;
Repository Interfaces
Repository interfaces define CRUD operations for entities.
Repository interface:
<?php
namespace Vendor\Blog\Api;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\Api\SearchResultsInterface;
use Vendor\Blog\Api\Data\PostInterface;
interface PostRepositoryInterface
{
/**
* Get post by ID
* @param int $id
* @return PostInterface
* @throws \Magento\Framework\Exception\NoSuchEntityException
*/
public function get($id): PostInterface;
/**
* Get list of posts
* @param SearchCriteriaInterface $searchCriteria
* @return SearchResultsInterface
*/
public function getList(SearchCriteriaInterface $searchCriteria): SearchResultsInterface;
/**
* Save post
* @param PostInterface $post
* @return PostInterface
*/
public function save(PostInterface $post): PostInterface;
/**
* Delete post
* @param PostInterface $post
* @return bool
*/
public function delete(PostInterface $post): bool;
/**
* Delete post by ID
* @param int $id
* @return bool
*/
public function deleteById($id): bool;
}
Service interface:
<?php
namespace Vendor\Blog\Api;
interface PostServiceInterface
{
public function publish(int $id): PostInterface;
public function archive(int $id): PostInterface;
public function duplicate(int $id): PostInterface;
}
Service Contract Benefits
Benefits and practical applications of service contracts.
1. API generation:
<!-- webapi.xml automatically uses service contracts -->
<route url="/V1/posts/:id" method="GET">
<service class="Vendor\Blog\Api\PostRepositoryInterface" method="get"/>
</route>
2. Testing:
// Mock the interface, not the implementation
$mockPost = $this->createMock(PostInterface::class);
$mockRepo = $this->createMock(PostRepositoryInterface::class);
$mockRepo->method('get')->willReturn($mockPost);
// Test against interface
$this->assertEquals($mockPost, $mockRepo->get(1));
3. Version compatibility:
// Interface remains stable across versions
// Implementation can change without breaking API
// Version 1.0
interface PostRepositoryInterface {
get($id);
save(PostInterface $post);
}
// Version 2.0 - same interface, new implementation
// Still works with old code
4. Third-party integration:
// Third-party modules can use your interfaces
public function processPost(
\Vendor\Blog\Api\PostRepositoryInterface $postRepo,
int $postId
) {
$post = $postRepo->get($postId);
// Process...
}
5. REST/GraphQL API:
# Service contracts automatically expose REST API
GET /rest/V1/posts/1
POST /rest/V1/posts
PUT /rest/V1/posts/1
DELETE /rest/V1/posts/1
Implementation binding:
<!-- di.xml -->
<preference for="Vendor\Blog\Api\PostRepositoryInterface"
type="Vendor\Blog\Model\PostRepository"/>
Quiz
1. What is the main purpose of service contracts?
2. Where are service contract interfaces placed?
3. What does the preference element in di.xml do?
4. What is a key benefit of service contracts?
Flashcards
Question
What are the three service contract layers?
Click to reveal answer
Answer
Data interfaces, Repository interfaces, Service interfaces
Question
Where are service contracts placed?
Click to reveal answer
Answer
Module/Api/ directory
Question
What does <preference> do in di.xml?
Click to reveal answer
Answer
Binds an interface to its concrete implementation
Question
Why use service contracts?
Click to reveal answer
Answer
Stable API, testability, version compatibility, API generation
Question
What do data interfaces define?
Click to reveal answer
Answer
Structure of data objects with get/set methods
Revision Notes
Key Takeaways
- 1. Service contracts define stable API interfaces for modules
- 2. Three layers: Data, Repository, Service interfaces
- 3. Interfaces go in Api/ directory
- 4. di.xml <preference> binds interface to implementation
- 5. Service contracts enable REST/GraphQL API generation
- 6. Key benefits: stability, testability, version compatibility
Interview Tips
- • Explain why service contracts are important
- • Describe the three layers of service contracts
- • Know how to create and bind interfaces
- • Discuss how service contracts enable API generation
Cheat Sheet
Service Contracts Cheat Sheet
Structure:
Api/
├── Data/
│ └── EntityInterface.php
├── EntityRepositoryInterface.php
└── EntityServiceInterface.php
Binding:
<preference for="Interface" type="Implementation"/>
Benefits:
- Stable API
- Testability
- Version compatibility
- REST/GraphQL generation
Data interface: Constants + get/set methods
Repository: get, getList, save, delete
Service: Business operations