three-cups
three-cups

Reputation: 4385

Scala domain object modeling

I'm creating a new domain object mode. In this model, there are Users and Posts. In the future there will be more models (e.g. Comments). I'm trying to learn how to do this using scala to it's full extent. Here's a naive implementation:

class User(val id: String, val creationDate: DateTime, val name: String, val email: String)
class Post(val id: String, val creationDate: DateTime, val user: User, val title: String, val body: String)

And here's another approach attempting to get rid of the duplicate id and creationDate.

class Model(val id: String, val creationDate: DateTime)
class User(id: String, creationDate: DateTime, val name: String, val email: String) extends Model(id, creationDate)
class Post(id: String, creationDate: DateTime, val user: User, val title: String, val body: String) extends Model(id, creationDate)

I'd like to moderate some of my domain objects. To do this, I'd like to add an isApproved: Boolean field.

class Model(val id: String, val creationDate: DateTime)
class User(id: String, creationDate: DateTime, val name: String, val email: String) extends Model(id, creationDate)
class Post(id: String, creationDate: DateTime, val user: User, val title: String, val body: String) extends Model(id, creationDate)

trait Moderated {
  val isApproved: Boolean
}

class ModeratedPost(id: String, creationDate: DateTime, val user: User, val title: String, val body: String, val isApproved: Boolean) extends Post(id, creationDate, user, title, body) with Moderated

I'd also like to prevent bugs in my code by type aliasing user and post Ids.

type Id = String
type UserId = Id
type PostId = Id

class Model(val id: Id, val creationDate: DateTime)
class User(id: UserId, creationDate: DateTime, val name: String, val email: String) extends Model(id, creationDate)
class Post(id: PostId, creationDate: DateTime, val user: User, val title: String, val body: String) extends Model(id, creationDate)

trait Moderated {
  val isApproved: Boolean
}

class ModeratedPost(id: String, creationDate: DateTime, val user: User, val title: String, val body: String, val isApproved: Boolean) extends Post(id, creationDate, user, title, body) with Moderated

At this point, I've got several questions.

Where should I define my type aliases? I think they have to be defined inside a class, trait, or object.

My goal in using type aliases for my Ids is to catch errors at compile time. I'd like UserId and PostId to be "subclasses" of Id. I.e. if a method took an Id, I could pass in a PostId. How should I do this?

My Moderated trait does not feel very useful. I still have to declare the isApproved on all classes that mix it in. Any tips here?

Upvotes: 1

Views: 1800

Answers (2)

Kevin Wright
Kevin Wright

Reputation: 49705

Idiomatic scala would go something like:

sealed trait Id { def strVal: String }
case class UserId(strVal: String) extends Id
case class PostId(strVal: String) extends Id

trait Model { def id: Id, def creationDate: DateTime)

case class User(
  id: UserId,
  creationDate: DateTime,
  name: String,
  email: String
) extends Model

trait Post extends model {
  def id: PostId
  def user: User,
  def title: String,
  def body: String
)

trait Moderated { def isApproved: Boolean }

case class UnmoderatedPost(
  id: PostId
  creationDate: DateTime,
  user: User,
  title: String,
  body: String,
) extends Post

case class ModeratedPost(
  id: PostId,
  creationDate: DateTime,
  user: User,
  title: String,
  body: String,
  isApproved: Boolean
) extends Post with Moderated

Upvotes: 1

reggoodwin
reggoodwin

Reputation: 1544

You can define your type aliases in package.scala which can be created for each package.

Lets say you have a simple package org.your.project. Create a file in directory org/your/project called: package.scala

package org.your.project

package object Types {

    type Id = String
    type UserId = Id
    type PostId = Id

}

Then in the class you wish to use the type aliases add:

import org.your.project.Types._

I'd probably not use types for the reasons you are thinking of. A type of Id could also be an Int but you made it a String. Anyone reading the code would have to click around the code base to figure out what the Id really is.

Upvotes: 1

Related Questions