avregi
avregi

Reputation: 137

Create class architecture based on ERD

To keep things simple: Let's say you have an entity relationship diagram (ERD) with the following tables:

  1. Survey: Id, name, questions
  2. Question: Id, text, survey

In this case a Survey has one or more Questions and a Question only belongs to one Survey.

My question is, can I recreate this architecture using classes only without any persistency layer like Realm or Core data? This is a simple demonstration of what I mean:

struct Survey {
    var id: Int
    var name: String
    var questions: [Question] // 1 or more questions
}

struct Question {
    var id: Int
    var text: String
    var survey: Survey // belongs to only one survey
}

I tried this approach but obviously the following code won't work due to the one to many relationship:

let survey = Survey(id: 0, name: "First survey", questions: [
        Question(id: survey.id, text: "Whats your name", survey: survey) // This is not possible.
])

More background: I want to create a survey application. The architecture of the app is difficult to design since the survey will support multiple types of questions (multiple choice, open questions, yes/no questions and so on). Doing an online search I found out there are a few ERD designs for a survey like this one. The problem is that this ERD design is for databases .. but I don't want to create a database for now. I assume however that architecturally the design can be represented using classes. So the question is how?

Upvotes: 1

Views: 110

Answers (2)

Christophe
Christophe

Reputation: 73456

Modelling the architecture

ERD is rooted in relational algebra which was made primarily for relational database modelling. You may nevertheless perfectly use it for modelling an architecture that does not use relational databases, and even without any persistence.

But ERD was invented in a time where OOP was quite exotic. Several extended ERD variants (EERD) exist, each trying to address inheritance and specialisation in its own way, but with a weak semantic and no authoritative standard.

Revising the architecture

A better approach would be to model your design with an UML class-diagram, with full support for OOP and wiki may look like:

enter image description here

Indeed, the intent to support "multiple types of questions" strongly suggest to use some specialisation (sub-types) of Question since the content and behavior of multiple choice is very different from an open question. Inheritance would then allow to apply the Open Closed Principle, i.e. easily add new types of questions without changing the rest of the code.

Consequence on your code: Question should not be a struct but a class or a protocol.

Does question need to know its survey?

Another point is to clarify why the Question would need to know about the Survey. I am not sure, and therefore I used a unidirectional navigation arrow. Indeed, one could easily imagine the same question being used in several surveys.

If you need it, your design is problematic, because it is difficult to initialise a survey with questions as your code does, and moreover, nothing would prevent inconsistencies, with a question referring to another survey than the one it is included in.

To solve this, consider, either a design without reference back, or at least to create the Survey and add questions in two steps: a method instead of directly modifying the array could enforce consistency. Alternatively, you could consider some creative use of didSet

Upvotes: 0

Sandeep
Sandeep

Reputation: 21154

You have dependencies between these two objects since initializer for both structs expect other to be created before, this is just a fault in design. You can tackle this kind of dependencies by using optionals or implicitly unwrapped optional (which is not safe).

Here is one obvious way to solve it,

struct Survey {
  var id: Int
  var name: String
  var questions: [Question]! // 1 or more questions

  init(id: Int, name: String) {
    self.id = id
    self.name = name
  }
}

struct Question {
  var id: Int
  var text: String
  var survey: Survey // belongs to only one survey
}

So, instead of passing object inside initializer, you could create one object without the need to create other struct and simply use assignment.

var survey = Survey(id: 0, name: "First survey")
survey.questions = [
    Question(id: survey.id, text: "Whats your name", survey:survey) // This is not possible.
]

Upvotes: 0

Related Questions