What we have here is known as a "Variant Entity". Purist design would require you to have 3 tables: Animals, Cats, and Dogs. Attributes that are not specific to cats or dogs would be in the Animals table, while attributes specific to cats in the Cats table and dogs/Dogs. The PK for all 3 tables would be the same with a relationship cardinality of "zero or one" (optional one-to-one). This model allows extensibility, allowing you to add more animal types to the model. The field in the variant tables acts as both PK and foreign key to Animals.
A constraint should be added to prevent any animal record having a record with related records in more than one table.
One possible alternative would be to have just one table with nulls allowed for the variant attributes. This would be frowned on by purists (like me!) and make extensibility dirty (how many nullable fields would you need?) and violates 3rd normal form.
A lot depends on what attributes you are storing for each variant (species).
Of course, implementation is likely to have lookup tables too (certainly "Species").
Here is script for SQL Server:
USE [DB_Animals]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Animals](
[AnimalID] [int] IDENTITY(1,1) NOT NULL,
[AnimalName] [varchar](32) NOT NULL,
[SpeciesID] [int] NOT NULL,
CONSTRAINT [PK_Animals] PRIMARY KEY CLUSTERED
(
[AnimalID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Dogs](
[AnimalID] [int] NOT NULL,
[DogBreedID] [int] NOT NULL,
[Barks] [bit] NOT NULL,
CONSTRAINT [PK_Dogs] PRIMARY KEY CLUSTERED
(
[AnimalID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Cats](
[AnimalID] [int] NOT NULL,
[CatBreedID] [int] NOT NULL,
[Purrs] [bit] NOT NULL,
CONSTRAINT [PK_Cats] PRIMARY KEY CLUSTERED
(
[Purrs] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER [dbo].[trg_SpeciesConstraint]
ON [dbo].[Cats]
AFTER INSERT
AS
BEGIN
DECLARE @OtherSpecies int
SELECT @OtherSpecies = COUNT(*) FROM Dogs INNER JOIN inserted ON inserted.AnimalID = Dogs.AnimalID
If @OtherSpecies > 0 Raiserror ('This animal is already another species and cannot be a cat',16,1)
END
GO
ALTER TABLE [dbo].[Cats] ADD CONSTRAINT [DF_Cats_Purrs] DEFAULT ((1)) FOR [Purrs]
GO
ALTER TABLE [dbo].[Dogs] ADD CONSTRAINT [DF_Dogs_Barks] DEFAULT ((1)) FOR [Barks]
GO
ALTER TABLE [dbo].[Cats] WITH CHECK ADD CONSTRAINT [FK_Cats_Animals] FOREIGN KEY([AnimalID])
REFERENCES [dbo].[Animals] ([AnimalID])
GO
ALTER TABLE [dbo].[Cats] CHECK CONSTRAINT [FK_Cats_Animals]
GO
ALTER TABLE [dbo].[Dogs] WITH CHECK ADD CONSTRAINT [FK_Dogs_Animals] FOREIGN KEY([AnimalID])
REFERENCES [dbo].[Animals] ([AnimalID])
GO
ALTER TABLE [dbo].[Dogs] CHECK CONSTRAINT [FK_Dogs_Animals]
GO
Notes:
Assumes some cats do not purr and some dogs do not bark to give variant attributes (also breed).
Lookup tables not included.
I used a trigger on Cats to enforce a constraint (you could code a Dogs trigger yourself and modify if there are more species).
You do not need IDs in the variant tables (they serve no purpose).