diff --git a/modules/project_browser_devel/src/Plugin/ProjectBrowserSource/RandomDataPlugin.php b/modules/project_browser_devel/src/Plugin/ProjectBrowserSource/RandomDataPlugin.php
index 95c657f58c64b666c4ad7e451c8785f025704f4c..5273f92b28a3c5329c1da0ac33b37fdf9b2857e0 100644
--- a/modules/project_browser_devel/src/Plugin/ProjectBrowserSource/RandomDataPlugin.php
+++ b/modules/project_browser_devel/src/Plugin/ProjectBrowserSource/RandomDataPlugin.php
@@ -122,17 +122,17 @@ class RandomDataPlugin extends ProjectBrowserSourceBase implements ContainerFact
 
     // Filter by project machine name.
     if (!empty($query['machine_name'])) {
-      $projects = array_filter($projects, fn(Project $project) => $project->getMachineName() === $query['machine_name']);
+      $projects = array_filter($projects, fn(Project $project) => $project->machineName === $query['machine_name']);
     }
 
     // Filter by categories.
     if (!empty($query['categories'])) {
-      $projects = array_filter($projects, fn(Project $project) => array_intersect(array_column($project->getModuleCategories(), 'id'), explode(',', $query['categories'])));
+      $projects = array_filter($projects, fn(Project $project) => array_intersect(array_column($project->categories, 'id'), explode(',', $query['categories'])));
     }
 
     // Filter by search text.
     if (!empty($query['search'])) {
-      $projects = array_filter($projects, fn(Project $project) => stripos($project->getTitle(), $query['search']) !== FALSE);
+      $projects = array_filter($projects, fn(Project $project) => stripos($project->title, $query['search']) !== FALSE);
     }
 
     return new ProjectsResultsPage(count($projects), array_values($projects), (string) $this->getPluginDefinition()['label'], $this->getPluginId(), TRUE);
@@ -172,36 +172,37 @@ class RandomDataPlugin extends ProjectBrowserSourceBase implements ContainerFact
         ];
       }
 
-      $projects[] = (new Project())
-        ->setAuthor([
-          'name' => $this->randomGenerator->word(10),
-        ])
-        ->setCreatedTimestamp($this->getRandomDate())
-        ->setChangedTimestamp($this->getRandomDate())
-        ->setProjectStatus(rand(0, 1))
-        ->setProjectTitle(ucwords($machine_name))
-        ->setId(uniqid())
-        ->setSummary([
-          'summary' => $this->randomGenerator->paragraphs(1),
-          'value' => $this->randomGenerator->paragraphs(5),
-        ])
-        ->setLogo([
+      $projects[] = new Project(
+        id: uniqid(),
+        logo: [
           'file' => [
             'uri' => ($i % 3) ? $good_image : $broken_image,
             'resource' => 'image',
           ],
           'alt' => $machine_name . ' logo',
-        ])
-        ->setImages($project_images)
-        ->setModuleCategories([$categories[array_rand($categories)]])
-        ->setMachineName($machine_name)
-        ->setComposerNamespace('random/' . $machine_name)
-        ->setIsCompatible((bool) ($i / 4))
-        ->setProjectUsageTotal(rand(0, 100000))
-        ->setProjectStarUserCount(rand(0, 100))
-        ->setIsCovered((bool) rand(0, 1))
-        ->setIsActive((bool) rand(0, 1))
-        ->setIsMaintained((bool) rand(0, 1));
+        ],
+        isCompatible: (bool) ($i / 4),
+        isMaintained: (bool) rand(0, 1),
+        isCovered: (bool) rand(0, 1),
+        isActive: (bool) rand(0, 1),
+        starUserCount: rand(0, 100),
+        projectUsageTotal: rand(0, 100000),
+        machineName: $machine_name,
+        body: [
+          'summary' => $this->randomGenerator->paragraphs(1),
+          'value' => $this->randomGenerator->paragraphs(5),
+        ],
+        title: ucwords($machine_name),
+        status: rand(0, 1),
+        changed: $this->getRandomDate(),
+        created: $this->getRandomDate(),
+        author: [
+          'name' => $this->randomGenerator->word(10),
+        ],
+        composerNamespace: 'random/' . $machine_name,
+        categories: [$categories[array_rand($categories)]],
+        images: $project_images,
+      );
     }
     $this->cacheBin->set('RandomData:projects', $projects);
     return $projects;
diff --git a/modules/project_browser_source_example/src/Plugin/ProjectBrowserSource/ProjectBrowserSourceExample.php b/modules/project_browser_source_example/src/Plugin/ProjectBrowserSource/ProjectBrowserSourceExample.php
index 08e59864363d454156cd06aa1e475d9cff8f0f82..b3b02645cc9fcef74c66d6621287a4df38bfe21d 100644
--- a/modules/project_browser_source_example/src/Plugin/ProjectBrowserSource/ProjectBrowserSourceExample.php
+++ b/modules/project_browser_source_example/src/Plugin/ProjectBrowserSource/ProjectBrowserSourceExample.php
@@ -92,32 +92,33 @@ class ProjectBrowserSourceExample extends ProjectBrowserSourceBase {
         ];
       }
 
-      $projects[] = (new Project())
-        ->setAuthor($author)
-        ->setCreatedTimestamp($project_from_source['created_at'])
-        ->setChangedTimestamp($project_from_source['updated_at'])
-        ->setProjectTitle($project_from_source['label'])
-        ->setId($project_from_source['identifier'])
-        ->setSummary([
-          'summary' => $project_from_source['short_description'],
-          'value' => $project_from_source['long_description'],
-        ])
-        ->setLogo($logo)
-        ->setModuleCategories($categories)
-        ->setMachineName($project_from_source['unique_name'])
-        ->setComposerNamespace($project_from_source['composer_namespace'])
+      $projects[] = new Project(
+        id: $project_from_source['identifier'],
+        logo: $logo,
         // Maybe the source won't have all fields, but we still need to
         // populate the values of all the properties.
-        ->setIsCompatible(TRUE)
-        ->setProjectUsageTotal(0)
-        ->setProjectStarUserCount(0)
-        // Images: Array of images using the same structure as $logo, above.
-        ->setImages([])
+        isCompatible: TRUE,
+        isMaintained: TRUE,
+        isCovered: TRUE,
+        isActive: TRUE,
+        starUserCount: 0,
+        projectUsageTotal: 0,
+        machineName: $project_from_source['unique_name'],
+        body: [
+          'summary' => $project_from_source['short_description'],
+          'value' => $project_from_source['long_description'],
+        ],
+        title: $project_from_source['label'],
         // Status: 1 enabled / 0 disabled.
-        ->setProjectStatus(1)
-        ->setIsCovered(TRUE)
-        ->setIsActive(TRUE)
-        ->setIsMaintained(TRUE);
+        status: 1,
+        changed: $project_from_source['updated_at'],
+        created: $project_from_source['created_at'],
+        author: $author,
+        composerNamespace: $project_from_source['composer_namespace'],
+        categories: $categories,
+        // Images: Array of images using the same structure as $logo, above.
+        images: [],
+      );
     }
 
     // Return one page of results. The first parameter is the total number of
diff --git a/src/Plugin/ProjectBrowserSource/DrupalCore.php b/src/Plugin/ProjectBrowserSource/DrupalCore.php
index b8df1a103638bb39729fd77f2b9a329dd3a1dbbe..a787c62128d7382974619168e461ca93f7085a65 100644
--- a/src/Plugin/ProjectBrowserSource/DrupalCore.php
+++ b/src/Plugin/ProjectBrowserSource/DrupalCore.php
@@ -119,22 +119,22 @@ class DrupalCore extends ProjectBrowserSourceBase implements ContainerFactoryPlu
 
     // Filter by project machine name.
     if (!empty($query['machine_name'])) {
-      $projects = array_filter($projects, fn(Project $project) => $project->getMachineName() === $query['machine_name']);
+      $projects = array_filter($projects, fn(Project $project) => $project->machineName === $query['machine_name']);
     }
 
     // Filter by coverage.
     if (!empty($query['security_advisory_coverage']) && $query['security_advisory_coverage'] === self::COVERED) {
-      $projects = array_filter($projects, fn(Project $project) => $project->isCovered());
+      $projects = array_filter($projects, fn(Project $project) => $project->isCovered);
     }
 
     // Filter by categories.
     if (!empty($query['categories'])) {
-      $projects = array_filter($projects, fn(Project $project) => array_intersect(array_column($project->getModuleCategories(), 'id'), explode(',', $query['categories'])));
+      $projects = array_filter($projects, fn(Project $project) => array_intersect(array_column($project->categories, 'id'), explode(',', $query['categories'])));
     }
 
     // Filter by search text.
     if (!empty($query['search'])) {
-      $projects = array_filter($projects, fn(Project $project) => stripos($project->getTitle(), $query['search']) !== FALSE);
+      $projects = array_filter($projects, fn(Project $project) => stripos($project->title, $query['search']) !== FALSE);
     }
 
     // Filter by sorting criterion.
@@ -142,11 +142,11 @@ class DrupalCore extends ProjectBrowserSourceBase implements ContainerFactoryPlu
       $sort = $query['sort'];
       switch ($sort) {
         case 'a_z':
-          usort($projects, fn($x, $y) => $x->getTitle() <=> $y->getTitle());
+          usort($projects, fn($x, $y) => $x->title <=> $y->title);
           break;
 
         case 'z_a':
-          usort($projects, fn($x, $y) => $y->getTitle() <=> $x->getTitle());
+          usort($projects, fn($x, $y) => $y->title <=> $x->title);
           break;
       }
     }
@@ -172,41 +172,42 @@ class DrupalCore extends ProjectBrowserSourceBase implements ContainerFactoryPlu
     foreach ($this->getCoreModules() as $module_name => $module) {
       // Dummy data is used for the fields that are unavailable for core
       // modules.
-      $returned_list[] = (new Project())
-        ->setProjectStatus($module->status)
-        ->setProjectTitle($module->info['name'])
-        ->setMachineName($module_name)
-        ->setLogo([
+      $returned_list[] = new Project(
+        id: 0,
+        logo: [
           'file' => [
             'uri' => \Drupal::request()->getSchemeAndHttpHost() . '/core/misc/logo/drupal-logo.svg',
             'resource' => 'image',
           ],
           'alt' => '',
-        ])
-        ->setAuthor([
-          'name' => 'Drupal Core',
-        ])
-        ->setSummary([
+        ],
+        // All core projects are considered compatible.
+        isCompatible: TRUE,
+        isMaintained: TRUE,
+        isCovered: $module->info['package'] !== 'Core (Experimental)',
+        isActive: TRUE,
+        starUserCount: -1,
+        projectUsageTotal: -1,
+        machineName: $module_name,
+        body: [
           'summary' => $module->info['description'],
           'value' => $module->info['description'],
-        ])
-        ->setModuleCategories([
+        ],
+        title: $module->info['name'],
+        status: $module->status,
+        changed: 280299600,
+        created: 280299600,
+        author: [
+          'name' => 'Drupal Core',
+        ],
+        composerNamespace: '',
+        categories: [
           [
             'id' => $module->info['package'],
             'name' => $module->info['package'],
           ],
-        ])
-        ->setCreatedTimestamp(280299600)
-        ->setChangedTimestamp(280299600)
-        ->setProjectUsageTotal(-1)
-        ->setProjectStarUserCount(-1)
-        ->setId(0)
-        ->setComposerNamespace('')
-        // All core projects are considered compatible.
-        ->setIsCompatible(TRUE)
-        ->setIsCovered($module->info['package'] !== 'Core (Experimental)')
-        ->setIsActive(TRUE)
-        ->setIsMaintained(TRUE);
+        ],
+      );
     }
 
     $this->cacheBin->set('DrupalCore:projects', $returned_list);
diff --git a/src/Plugin/ProjectBrowserSource/MockDrupalDotOrg.php b/src/Plugin/ProjectBrowserSource/MockDrupalDotOrg.php
index a802b04545107088760a61c26f7043b411b6f360..6973dc5c4f988394ced3ce3751c57cecae3824e7 100644
--- a/src/Plugin/ProjectBrowserSource/MockDrupalDotOrg.php
+++ b/src/Plugin/ProjectBrowserSource/MockDrupalDotOrg.php
@@ -362,30 +362,31 @@ class MockDrupalDotOrg extends ProjectBrowserSourceBase implements ContainerFact
           'alt' => 'Project logo',
         ];
 
-        $returned_list[] = (new Project())
-          ->setId($project_data['nid'])
-          ->setProjectTitle($project_data['title'])
-          ->setProjectStatus($project_data['status'])
-          ->setProjectUrl('https://www.drupal.org/project/' . $project_data['field_project_machine_name'])
-          ->setChangedTimestamp($project_data['changed'])
-          ->setCreatedTimestamp($project_data['created'])
-          ->setAuthor(['name' => $project_data['author']])
-          // Add name property to each category, so it can be rendered.
-          ->setModuleCategories(array_map(fn($category) => $categories[$category['id']] ?? '', $project_data['project_data']['taxonomy_vocabulary_3'] ?? []))
+        $returned_list[] = new Project(
+          id: $project_data['nid'],
+          logo: $logo,
           // Mock projects are filtered and made sure that they are compatible
           // before we even put them in the database.
-          ->setIsCompatible(TRUE)
-          ->setProjectUsageTotal(array_reduce($project_data['project_data']['project_usage'] ?? [], fn($total, $project_usage) => $total + $project_usage) ?: 0)
-          ->setLogo($logo)
-          ->setImages($project_data['project_data']['field_project_images'] ?? [])
-          ->setSummary($this->relativeToAbsoluteUrls($project_data['project_data']['body'], 'https://www.drupal.org'))
-          ->setIsCovered(in_array($project_data['field_security_advisory_coverage'], self::COVERED_VALUES))
-          ->setIsActive(in_array($project_data['development_status'], self::ACTIVE_VALUES))
-          ->setIsMaintained(in_array($project_data['maintenance_status'], self::MAINTAINED_VALUES))
-          ->setWarnings($this->getWarnings($project_data))
-          ->setMachineName($project_data['field_project_machine_name'])
-          ->setComposerNamespace('drupal/' . $project_data['field_project_machine_name'])
-          ->setProjectStarUserCount(-1);
+          isCompatible: TRUE,
+          isMaintained: in_array($project_data['maintenance_status'], self::MAINTAINED_VALUES),
+          isCovered: in_array($project_data['field_security_advisory_coverage'], self::COVERED_VALUES),
+          isActive: in_array($project_data['development_status'], self::ACTIVE_VALUES),
+          starUserCount: -1,
+          projectUsageTotal: array_reduce($project_data['project_data']['project_usage'] ?? [], fn($total, $project_usage) => $total + $project_usage) ?: 0,
+          machineName: $project_data['field_project_machine_name'],
+          body: $this->relativeToAbsoluteUrls($project_data['project_data']['body'], 'https://www.drupal.org'),
+          title: $project_data['title'],
+          status: $project_data['status'],
+          changed: $project_data['changed'],
+          created: $project_data['created'],
+          author: ['name' => $project_data['author']],
+          composerNamespace: 'drupal/' . $project_data['field_project_machine_name'],
+          url: 'https://www.drupal.org/project/' . $project_data['field_project_machine_name'],
+          // Add name property to each category, so it can be rendered.
+          categories: array_map(fn($category) => $categories[$category['id']] ?? '', $project_data['project_data']['taxonomy_vocabulary_3'] ?? []),
+          images: $project_data['project_data']['field_project_images'] ?? [],
+          warnings: $this->getWarnings($project_data),
+        );
       }
     }
 
diff --git a/src/ProjectBrowser/Project.php b/src/ProjectBrowser/Project.php
index b6f1b584f31dcb42ed9bdf75033d76712f11c8a3..3c42c421f70d31addc4e306fde242ee5e2995d4a 100644
--- a/src/ProjectBrowser/Project.php
+++ b/src/ProjectBrowser/Project.php
@@ -11,221 +11,72 @@ use Drupal\Component\Utility\Unicode;
 class Project implements \JsonSerializable {
 
   /**
-   * Logo of the project.
-   *
-   * @var array
-   */
-  private array $logo;
-
-  /**
-   * Whether the project is compatible with the current version of Drupal.
-   *
-   * @var bool
-   */
-  private bool $isCompatible;
-
-  /**
-   * Whether the project is considered to be maintained or not.
-   *
-   * @var bool
-   */
-  private bool $isMaintained;
-
-  /**
-   * Whether the project is considered to be covered or not.
-   *
-   * @var bool
-   */
-  private bool $isCovered;
-
-  /**
-   * Whether the project is considered to be active or not.
-   *
-   * @var bool
-   */
-  private bool $isActive;
-
-  /**
-   * User start count of the project.
-   *
-   * @var int
-   */
-  private int $starUserCount;
-
-  /**
-   * Total usage of the project.
-   *
-   * @var int
-   */
-  private int $projectUsageTotal;
-
-  /**
-   * URL of the project.
-   *
-   * @var string
-   */
-  private string $url = '';
-
-  /**
-   * Value of module_categories of the project.
-   *
-   * @var array
-   */
-  private array $categories = [];
-
-  /**
-   * Value of project_machine_name of the project.
-   *
-   * @var string
-   */
-  private string $machineName;
-
-  /**
-   * Images of the project.
-   *
-   * @var array
-   */
-  private array $images = [];
-
-  /**
-   * Body field of the project in array format.
-   *
-   * @var array
-   */
-  private array $body;
-
-  /**
-   * ID of the project.
-   *
-   * @var string
-   */
-  private string $id;
-
-  /**
-   * Title of the project.
-   *
-   * @var string
-   */
-  private string $title;
-
-  /**
-   * Status of the project.
-   *
-   * @var int
-   */
-  private int $status;
-
-  /**
-   * When was the project changed last timestamp.
-   *
-   * @var int
-   */
-  private int $changed;
-
-  /**
-   * When was the project created last timestamp.
-   *
-   * @var int
-   */
-  private int $created;
-
-  /**
-   * Author of the project in array format.
-   *
-   * @var array
-   */
-  private array $author;
-
-  /**
-   * Warnings for the project.
-   *
-   * @var array
-   */
-  private array $warnings = [];
-
-  /**
-   * Composer namespace of the project.
-   *
-   * @var string
-   */
-  private string $composerNamespace;
-
-  /**
-   * Set the author of Project.
-   *
-   * @param array $author
-   *   Author in array format.
-   *
-   * @return $this
-   */
-  public function setAuthor(array $author) {
-    $this->author = $author;
-    return $this;
-  }
-
-  /**
-   * Set the project created timestamp.
-   *
-   * @param int $created
-   *   Timestamp.
-   *
-   * @return $this
-   */
-  public function setCreatedTimestamp(int $created) {
-    $this->created = $created;
-    return $this;
-  }
-
-  /**
-   * Set the project changed timestamp.
-   *
-   * @param int $changed
-   *   Timestamp.
-   *
-   * @return $this
-   */
-  public function setChangedTimestamp(int $changed) {
-    $this->changed = $changed;
-    return $this;
-  }
-
-  /**
-   * Set the project status.
-   *
-   * @param int $status
-   *   Status of the project.
-   *
-   * @return $this
-   */
-  public function setProjectStatus(int $status) {
-    $this->status = $status;
-    return $this;
-  }
-
-  /**
-   * Set the project title.
-   *
-   * @param string $title
-   *   Title of the project.
-   *
-   * @return $this
-   */
-  public function setProjectTitle(string $title) {
-    $this->title = $title;
-    return $this;
-  }
-
-  /**
-   * Set the unique identifier of Project, eg nid.
+   * Constructs a Project object.
    *
    * @param string $id
    *   ID of the project.
-   *
-   * @return $this
-   */
-  public function setId(string $id) {
-    $this->id = $id;
-    return $this;
+   * @param array $logo
+   *   Logo of the project.
+   * @param bool $isCompatible
+   *   Whether the project is compatible with the current version of Drupal.
+   * @param bool $isMaintained
+   *   Whether the project is considered to be maintained or not.
+   * @param bool $isCovered
+   *   Whether the project is considered to be covered or not.
+   * @param bool $isActive
+   *   Whether the project is considered to be active or not.
+   * @param int $starUserCount
+   *   User start count of the project.
+   * @param int $projectUsageTotal
+   *   Total usage of the project.
+   * @param string $machineName
+   *   Value of project_machine_name of the project.
+   * @param array $body
+   *   Body field of the project in array format.
+   * @param string $title
+   *   Title of the project.
+   * @param int $status
+   *   Status of the project.
+   * @param int $changed
+   *   When was the project changed last timestamp.
+   * @param int $created
+   *   When was the project created last timestamp.
+   * @param array $author
+   *   Author of the project in array format.
+   * @param string $composerNamespace
+   *   Composer namespace of the project.
+   * @param string $url
+   *   URL of the project.
+   * @param array $categories
+   *   Value of module_categories of the project.
+   * @param array $images
+   *   Images of the project.
+   * @param array $warnings
+   *   Warnings for the project.
+   */
+  public function __construct(
+    public string $id,
+    public array $logo,
+    public bool $isCompatible,
+    public bool $isMaintained,
+    public bool $isCovered,
+    public bool $isActive,
+    public int $starUserCount,
+    public int $projectUsageTotal,
+    public string $machineName,
+    public array $body,
+    public string $title,
+    public int $status,
+    public int $changed,
+    public int $created,
+    public array $author,
+    public string $composerNamespace,
+    public string $url = '',
+    public array $categories = [],
+    public array $images = [],
+    public array $warnings = [],
+  ) {
+    $this->setSummary($body);
   }
 
   /**
@@ -246,205 +97,6 @@ class Project implements \JsonSerializable {
     return $this;
   }
 
-  /**
-   * Set the images associated with the project.
-   *
-   * @param array $images
-   *   Images in array format.
-   *
-   * @return $this
-   */
-  public function setImages(array $images) {
-    $this->images = $images;
-    return $this;
-  }
-
-  /**
-   * Set the project machine name.
-   *
-   * @param string $machine_name
-   *   Machine name of the module.
-   *
-   * @return $this
-   */
-  public function setMachineName(string $machine_name) {
-    $this->machineName = $machine_name;
-    return $this;
-  }
-
-  /**
-   * Set the project logo.
-   *
-   * @param array $logo
-   *   Logo in array format.
-   *
-   * @return $this
-   */
-  public function setLogo(array $logo) {
-    $this->logo = $logo;
-    return $this;
-  }
-
-  /**
-   * Set the composer namespace.
-   *
-   * @param string $composer_namespace
-   *   Composer namespace of the module.
-   *
-   * @return $this
-   */
-  public function setComposerNamespace(string $composer_namespace) {
-    $this->composerNamespace = $composer_namespace;
-    return $this;
-  }
-
-  /**
-   * Set the categories this project belongs.
-   *
-   * @param array $categories
-   *   Module category ids.
-   *
-   * @return $this
-   */
-  public function setModuleCategories(array $categories) {
-    $this->categories = $categories;
-    return $this;
-  }
-
-  /**
-   * Set the URL to the project page, where someone could learn more about this project.
-   *
-   * @param string $url
-   *   The URL.
-   *
-   * @return $this
-   */
-  public function setProjectUrl(string $url) {
-    $this->url = $url;
-    return $this;
-  }
-
-  /**
-   * Set the total usage count of all releases.
-   *
-   * @param int $usage_total
-   *   Total usage.
-   *
-   * @return $this
-   */
-  public function setProjectUsageTotal(int $usage_total) {
-    $this->projectUsageTotal = $usage_total;
-    return $this;
-  }
-
-  /**
-   * Set the project star user count.
-   *
-   * @param int $star_user_count
-   *   Start user count value.
-   *
-   * @return $this
-   */
-  public function setProjectStarUserCount(int $star_user_count) {
-    $this->starUserCount = $star_user_count;
-    return $this;
-  }
-
-  /**
-   * Set if the project is considered active or not.
-   *
-   * @param bool $is_active
-   *   Value to set.
-   *
-   * @return $this
-   */
-  public function setIsActive(bool $is_active) {
-    $this->isActive = $is_active;
-    return $this;
-  }
-
-  /**
-   * Set if the project is considered covered or not.
-   *
-   * @param bool $is_covered
-   *   Value to set.
-   *
-   * @return $this
-   */
-  public function setIsCovered(bool $is_covered) {
-    $this->isCovered = $is_covered;
-    return $this;
-  }
-
-  /**
-   * Set if the project is considered maintained or not.
-   *
-   * @param bool $is_maintained
-   *   Value to set.
-   *
-   * @return $this
-   */
-  public function setIsMaintained(bool $is_maintained) {
-    $this->isMaintained = $is_maintained;
-    return $this;
-  }
-
-  /**
-   * Set whether the project is compatible with the current Drupal installation.
-   *
-   * @param bool $compatible
-   *   Whether the project is compatible or not.
-   *
-   * @return $this
-   */
-  public function setIsCompatible(bool $compatible) {
-    $this->isCompatible = $compatible;
-    return $this;
-  }
-
-  /**
-   * Warnings related to installing a given module.
-   *
-   * @param string[] $warnings
-   *   Warnings about the module to present the the user.
-   *
-   * @return $this
-   */
-  public function setWarnings(array $warnings) {
-    $this->warnings = $warnings;
-    return $this;
-  }
-
-  /**
-   * Returns the machine name of the project.
-   *
-   * @return string
-   *   The machine name of the project.
-   */
-  public function getMachineName(): string {
-    return $this->machineName;
-  }
-
-  /**
-   * Returns the categories of the project.
-   *
-   * @return array
-   *   The categories of the project.
-   */
-  public function getModuleCategories(): array {
-    return $this->categories;
-  }
-
-  /**
-   * Returns the title of the project.
-   *
-   * @return string
-   *   The title of the project.
-   */
-  public function getTitle(): string {
-    return $this->title;
-  }
-
   /**
    * Returns the selector id of the project.
    *
@@ -455,16 +107,6 @@ class Project implements \JsonSerializable {
     return str_replace('_', '-', $this->machineName);
   }
 
-  /**
-   * Returns whether the project is considered covered or not.
-   *
-   * @return bool
-   *   Covered status.
-   */
-  public function isCovered(): bool {
-    return $this->isCovered;
-  }
-
   /**
    * {@inheritdoc}
    */
@@ -472,14 +114,14 @@ class Project implements \JsonSerializable {
   public function jsonSerialize() {
     return (object) [
       'is_compatible' => $this->isCompatible,
-      'is_covered' => $this->isCovered(),
+      'is_covered' => $this->isCovered,
       'project_usage_total' => $this->projectUsageTotal,
-      'module_categories' => $this->getModuleCategories(),
-      'project_machine_name' => $this->getMachineName(),
+      'module_categories' => $this->categories,
+      'project_machine_name' => $this->machineName,
       'project_images' => $this->images,
       'logo' => $this->logo,
       'body' => $this->body,
-      'title' => $this->getTitle(),
+      'title' => $this->title,
       'author' => $this->author,
       'warnings' => $this->warnings,
       'composer_namespace' => $this->composerNamespace,
diff --git a/tests/modules/project_browser_test/src/Plugin/ProjectBrowserSource/DrupalDotOrgJsonApi.php b/tests/modules/project_browser_test/src/Plugin/ProjectBrowserSource/DrupalDotOrgJsonApi.php
index f4d0f11e59a141639893a29e998ff14876822d63..2674be7439198a7f0371552ddfe11732e917bf96 100644
--- a/tests/modules/project_browser_test/src/Plugin/ProjectBrowserSource/DrupalDotOrgJsonApi.php
+++ b/tests/modules/project_browser_test/src/Plugin/ProjectBrowserSource/DrupalDotOrgJsonApi.php
@@ -372,30 +372,31 @@ class DrupalDotOrgJsonApi extends ProjectBrowserSourceBase implements ContainerF
           ];
         }
 
-        $body = $this->bodyRelativeToAbsoluteUrls($project['attributes']['body'] ?? ['summary' => '', 'value' => ''], 'https://www.drupal.org');
-        $project_object = new Project();
-        $project_object
-          ->setAuthor([
-            'name' => $related[$uid_info['type']][$uid_info['id']]['name'],
-          ])
-          ->setMachineName($machine_name)
-          ->setModuleCategories($module_categories)
-          ->setIsCovered(in_array($project['attributes']['field_security_advisory_coverage'], self::COVERED_VALUES))
-          ->setIsActive(in_array($development_status['id'], self::ACTIVE_VALUES))
-          ->setIsMaintained(in_array($maintenance_status['id'], self::MAINTAINED_VALUES))
-          ->setCreatedTimestamp(strtotime($project['attributes']['created']))
-          ->setChangedTimestamp(strtotime($project['attributes']['changed']))
-          ->setProjectStatus($project['attributes']['status'])
-          ->setProjectTitle($project['attributes']['title'])
-          ->setId($project['id'])
-          ->setSummary($body)
-          ->setLogo($logo)
-          ->setImages($project_images)
-          ->setIsCompatible($is_compatible)
-          ->setProjectUsageTotal($project_usage_total)
+        $body = $this->bodyRelativeToAbsoluteUrls(
+          $project['attributes']['body'] ?? ['summary' => '', 'value' => ''], 'https://www.drupal.org');
+        $project_object = new Project(
+          id: $project['id'],
+          logo: $logo,
+          isCompatible: $is_compatible,
+          isMaintained: in_array($maintenance_status['id'], self::MAINTAINED_VALUES),
+          isCovered: in_array($project['attributes']['field_security_advisory_coverage'], self::COVERED_VALUES),
+          isActive: in_array($development_status['id'], self::ACTIVE_VALUES),
           // Property not migrated to D9.
-          ->setProjectStarUserCount(0)
-          ->setComposerNamespace($project['attributes']['field_composer_namespace'] ?? 'drupal/' . $machine_name);
+          starUserCount: 0,
+          projectUsageTotal: $project_usage_total,
+          machineName: $machine_name,
+          body: $body,
+          title: $project['attributes']['title'],
+          status: $project['attributes']['status'],
+          changed: strtotime($project['attributes']['changed']),
+          created: strtotime($project['attributes']['created']),
+          author: [
+            'name' => $related[$uid_info['type']][$uid_info['id']]['name'],
+          ],
+          composerNamespace: $project['attributes']['field_composer_namespace'] ?? 'drupal/' . $machine_name,
+          categories: $module_categories,
+          images: $project_images,
+        );
         $returned_list[] = $project_object;
       }
     }
diff --git a/tests/src/Kernel/CoreExperimentalLabelTest.php b/tests/src/Kernel/CoreExperimentalLabelTest.php
index d06287949dc872018f307f2539edfaebb0666d5b..071827a69c3c5548964af67bd31415dd64bb2787 100644
--- a/tests/src/Kernel/CoreExperimentalLabelTest.php
+++ b/tests/src/Kernel/CoreExperimentalLabelTest.php
@@ -37,14 +37,14 @@ class CoreExperimentalLabelTest extends KernelTestBase {
     /** @var \Drupal\project_browser\Plugin\ProjectBrowserSourceInterface $plugin_instance */
     $plugin_instance = $this->container->get('plugin.manager.project_browser.source')->createInstance('drupal_core');
     $modules_to_test = ['Workspaces', 'System'];
-    $filtered_projects = array_filter($plugin_instance->getProjects()->list, fn(Project $value) => in_array($value->getTitle(), $modules_to_test));
+    $filtered_projects = array_filter($plugin_instance->getProjects()->list, fn(Project $value) => in_array($value->title, $modules_to_test));
     $this->assertCount(2, $filtered_projects);
     foreach ($filtered_projects as $project) {
-      if ($project->getTitle() === 'System') {
-        $this->assertTrue($project->isCovered());
+      if ($project->title === 'System') {
+        $this->assertTrue($project->isCovered);
       }
-      elseif ($project->getTitle() === 'Workspaces') {
-        $this->assertFalse($project->isCovered());
+      elseif ($project->title === 'Workspaces') {
+        $this->assertFalse($project->isCovered);
       }
     }
   }